From 279692afea9e8929d1364f0f6515de4dd68acd17 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Sun, 17 Sep 2023 17:06:24 -0400 Subject: [PATCH 1/6] WireGuard rework for Linux --- client/daemon/daemonlocalserver.cpp | 11 +- client/mozilla/networkwatcher.cpp | 4 +- client/mozilla/shared/ipaddress.cpp | 2 +- .../platforms/linux/daemon/dbustypeslinux.h | 170 ++++++++ .../platforms/linux/daemon/dnsutilslinux.cpp | 212 ++++++++++ client/platforms/linux/daemon/dnsutilslinux.h | 41 ++ .../platforms/linux/daemon/iputilslinux.cpp | 150 +++++++ client/platforms/linux/daemon/iputilslinux.h | 31 ++ client/platforms/linux/daemon/linuxdaemon.cpp | 52 +++ client/platforms/linux/daemon/linuxdaemon.h | 36 ++ .../linux/daemon/linuxroutemonitor.cpp | 133 +++++++ .../linux/daemon/linuxroutemonitor.h | 53 +++ client/platforms/linux/daemon/pidtracker.cpp | 228 +++++++++++ client/platforms/linux/daemon/pidtracker.h | 72 ++++ .../linux/daemon/wireguardutilslinux.cpp | 369 ++++++++++++++++++ .../linux/daemon/wireguardutilslinux.h | 55 +++ client/platforms/linux/interfaceconfig.h | 47 +++ client/platforms/linux/linuxdependencies.cpp | 139 +++++++ client/platforms/linux/linuxdependencies.h | 25 ++ .../platforms/linux/linuxnetworkwatcher.cpp | 57 +++ client/platforms/linux/linuxnetworkwatcher.h | 38 ++ .../linux/linuxnetworkwatcherworker.cpp | 177 +++++++++ .../linux/linuxnetworkwatcherworker.h | 41 ++ client/protocols/wireguardprotocol.cpp | 12 +- client/protocols/wireguardprotocol.h | 4 +- service/server/CMakeLists.txt | 27 +- service/server/localserver.cpp | 6 +- service/server/localserver.h | 16 +- service/server/router_linux.cpp | 146 ++++++- service/server/router_linux.h | 1 + 30 files changed, 2319 insertions(+), 36 deletions(-) create mode 100644 client/platforms/linux/daemon/dbustypeslinux.h create mode 100644 client/platforms/linux/daemon/dnsutilslinux.cpp create mode 100644 client/platforms/linux/daemon/dnsutilslinux.h create mode 100644 client/platforms/linux/daemon/iputilslinux.cpp create mode 100644 client/platforms/linux/daemon/iputilslinux.h create mode 100644 client/platforms/linux/daemon/linuxdaemon.cpp create mode 100644 client/platforms/linux/daemon/linuxdaemon.h create mode 100644 client/platforms/linux/daemon/linuxroutemonitor.cpp create mode 100644 client/platforms/linux/daemon/linuxroutemonitor.h create mode 100644 client/platforms/linux/daemon/pidtracker.cpp create mode 100644 client/platforms/linux/daemon/pidtracker.h create mode 100644 client/platforms/linux/daemon/wireguardutilslinux.cpp create mode 100644 client/platforms/linux/daemon/wireguardutilslinux.h create mode 100644 client/platforms/linux/interfaceconfig.h create mode 100644 client/platforms/linux/linuxdependencies.cpp create mode 100644 client/platforms/linux/linuxdependencies.h create mode 100644 client/platforms/linux/linuxnetworkwatcher.cpp create mode 100644 client/platforms/linux/linuxnetworkwatcher.h create mode 100644 client/platforms/linux/linuxnetworkwatcherworker.cpp create mode 100644 client/platforms/linux/linuxnetworkwatcherworker.h diff --git a/client/daemon/daemonlocalserver.cpp b/client/daemon/daemonlocalserver.cpp index 02a12cb9..9d8feb68 100644 --- a/client/daemon/daemonlocalserver.cpp +++ b/client/daemon/daemonlocalserver.cpp @@ -12,7 +12,7 @@ #include "leakdetector.h" #include "logger.h" -#ifdef MZ_MACOS +#if defined(MZ_MACOS) || defined(MZ_LINUX) # include # include # include @@ -68,7 +68,8 @@ bool DaemonLocalServer::initialize() { QString DaemonLocalServer::daemonPath() const { #if defined(MZ_WINDOWS) return "\\\\.\\pipe\\amneziavpn"; -#elif defined(MZ_MACOS) +#endif +#if defined(MZ_MACOS) || defined(MZ_LINUX) QDir dir("/var/run"); if (!dir.exists()) { logger.warning() << "/var/run doesn't exist. Fallback /tmp."; @@ -76,12 +77,12 @@ QString DaemonLocalServer::daemonPath() const { } if (dir.exists("amneziavpn")) { - logger.debug() << "/var/run/amneziavpn seems to be usable"; + logger.debug() << "/var/run/amnezia seems to be usable"; return VAR_PATH; } if (!dir.mkdir("amneziavpn")) { - logger.warning() << "Failed to create /var/run/amneziavpn"; + logger.warning() << "Failed to create /var/run/amnezia"; return TMP_PATH; } @@ -92,7 +93,5 @@ QString DaemonLocalServer::daemonPath() const { } return VAR_PATH; -#else -# error Unsupported platform #endif } diff --git a/client/mozilla/networkwatcher.cpp b/client/mozilla/networkwatcher.cpp index 54beb11c..47fdb622 100644 --- a/client/mozilla/networkwatcher.cpp +++ b/client/mozilla/networkwatcher.cpp @@ -19,7 +19,7 @@ #endif #ifdef MZ_LINUX -//# include "platforms/linux/linuxnetworkwatcher.h" +# include "platforms/linux/linuxnetworkwatcher.h" #endif #ifdef MZ_MACOS @@ -56,7 +56,7 @@ void NetworkWatcher::initialize() { #if defined(MZ_WINDOWS) m_impl = new WindowsNetworkWatcher(this); #elif defined(MZ_LINUX) -// m_impl = new LinuxNetworkWatcher(this); + m_impl = new LinuxNetworkWatcher(this); #elif defined(MZ_MACOS) m_impl = new MacOSNetworkWatcher(this); #elif defined(MZ_WASM) diff --git a/client/mozilla/shared/ipaddress.cpp b/client/mozilla/shared/ipaddress.cpp index 7c337755..1f84ad07 100644 --- a/client/mozilla/shared/ipaddress.cpp +++ b/client/mozilla/shared/ipaddress.cpp @@ -30,7 +30,7 @@ IPAddress::IPAddress(const QString& ip) { m_prefixLength = 128; } } else { - Q_ASSERT(false); + // Q_ASSERT(false); } } diff --git a/client/platforms/linux/daemon/dbustypeslinux.h b/client/platforms/linux/daemon/dbustypeslinux.h new file mode 100644 index 00000000..1a5e44e2 --- /dev/null +++ b/client/platforms/linux/daemon/dbustypeslinux.h @@ -0,0 +1,170 @@ +/* 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/. */ + +#ifndef DBUSTYPESLINUX_H +#define DBUSTYPESLINUX_H + +#include + +#include +#include +#include +#include + +/* D-Bus metatype for marshalling arguments to the SetLinkDNS method */ +class DnsResolver : public QHostAddress { + public: + DnsResolver(const QHostAddress& address = QHostAddress()) + : QHostAddress(address) {} + + friend QDBusArgument& operator<<(QDBusArgument& args, const DnsResolver& ip) { + args.beginStructure(); + if (ip.protocol() == QAbstractSocket::IPv6Protocol) { + Q_IPV6ADDR addrv6 = ip.toIPv6Address(); + args << AF_INET6; + args << QByteArray::fromRawData((const char*)&addrv6, sizeof(addrv6)); + } else { + quint32 addrv4 = ip.toIPv4Address(); + QByteArray data(4, 0); + data[0] = (addrv4 >> 24) & 0xff; + data[1] = (addrv4 >> 16) & 0xff; + data[2] = (addrv4 >> 8) & 0xff; + data[3] = (addrv4 >> 0) & 0xff; + args << AF_INET; + args << data; + } + args.endStructure(); + return args; + } + friend const QDBusArgument& operator>>(const QDBusArgument& args, + DnsResolver& ip) { + int family; + QByteArray data; + args.beginStructure(); + args >> family >> data; + args.endStructure(); + if (family == AF_INET6) { + ip.setAddress(data.constData()); + } else if (data.count() >= 4) { + quint32 addrv4 = 0; + addrv4 |= (data[0] << 24); + addrv4 |= (data[1] << 16); + addrv4 |= (data[2] << 8); + addrv4 |= (data[3] << 0); + ip.setAddress(addrv4); + } + return args; + } +}; +typedef QList DnsResolverList; +Q_DECLARE_METATYPE(DnsResolver); +Q_DECLARE_METATYPE(DnsResolverList); + +/* D-Bus metatype for marshalling arguments to the SetLinkDomains method */ +class DnsLinkDomain { + public: + DnsLinkDomain(const QString d = "", bool s = false) { + domain = d; + search = s; + }; + QString domain; + bool search; + + friend QDBusArgument& operator<<(QDBusArgument& args, + const DnsLinkDomain& data) { + args.beginStructure(); + args << data.domain << data.search; + args.endStructure(); + return args; + } + friend const QDBusArgument& operator>>(const QDBusArgument& args, + DnsLinkDomain& data) { + args.beginStructure(); + args >> data.domain >> data.search; + args.endStructure(); + return args; + } + bool operator==(const DnsLinkDomain& other) const { + return (domain == other.domain) && (search == other.search); + } + bool operator==(const QString& other) const { return (domain == other); } +}; +typedef QList DnsLinkDomainList; +Q_DECLARE_METATYPE(DnsLinkDomain); +Q_DECLARE_METATYPE(DnsLinkDomainList); + +/* D-Bus metatype for marshalling the Domains property */ +class DnsDomain { + public: + DnsDomain() {} + int ifindex = 0; + QString domain = ""; + bool search = false; + + friend QDBusArgument& operator<<(QDBusArgument& args, const DnsDomain& data) { + args.beginStructure(); + args << data.ifindex << data.domain << data.search; + args.endStructure(); + return args; + } + friend const QDBusArgument& operator>>(const QDBusArgument& args, + DnsDomain& data) { + args.beginStructure(); + args >> data.ifindex >> data.domain >> data.search; + args.endStructure(); + return args; + } +}; +typedef QList DnsDomainList; +Q_DECLARE_METATYPE(DnsDomain); +Q_DECLARE_METATYPE(DnsDomainList); + +/* D-Bus metatype for marshalling the freedesktop login manager data. */ +class UserData { + public: + QString name; + uint userid; + QDBusObjectPath path; + + friend QDBusArgument& operator<<(QDBusArgument& args, const UserData& data) { + args.beginStructure(); + args << data.userid << data.name << data.path; + args.endStructure(); + return args; + } + friend const QDBusArgument& operator>>(const QDBusArgument& args, + UserData& data) { + args.beginStructure(); + args >> data.userid >> data.name >> data.path; + args.endStructure(); + return args; + } +}; +typedef QList UserDataList; +Q_DECLARE_METATYPE(UserData); +Q_DECLARE_METATYPE(UserDataList); + +class DnsMetatypeRegistrationProxy { + public: + DnsMetatypeRegistrationProxy() { + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + qRegisterMetaType(); + qDBusRegisterMetaType(); + } +}; + +#endif // DBUSTYPESLINUX_H diff --git a/client/platforms/linux/daemon/dnsutilslinux.cpp b/client/platforms/linux/daemon/dnsutilslinux.cpp new file mode 100644 index 00000000..cc47202b --- /dev/null +++ b/client/platforms/linux/daemon/dnsutilslinux.cpp @@ -0,0 +1,212 @@ +/* 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 "dnsutilslinux.h" + +#include + +#include +#include + +#include "leakdetector.h" +#include "logger.h" + +constexpr const char* DBUS_RESOLVE_SERVICE = "org.freedesktop.resolve1"; +constexpr const char* DBUS_RESOLVE_PATH = "/org/freedesktop/resolve1"; +constexpr const char* DBUS_RESOLVE_MANAGER = "org.freedesktop.resolve1.Manager"; +constexpr const char* DBUS_PROPERTY_INTERFACE = + "org.freedesktop.DBus.Properties"; + +namespace { +Logger logger("DnsUtilsLinux"); +} + +DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) { + MZ_COUNT_CTOR(DnsUtilsLinux); + logger.debug() << "DnsUtilsLinux created."; + + QDBusConnection conn = QDBusConnection::systemBus(); + m_resolver = new QDBusInterface(DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, + DBUS_RESOLVE_MANAGER, conn, this); +} + +DnsUtilsLinux::~DnsUtilsLinux() { + MZ_COUNT_DTOR(DnsUtilsLinux); + + for (auto iterator = m_linkDomains.constBegin(); + iterator != m_linkDomains.constEnd(); ++iterator) { + QList argumentList; + argumentList << QVariant::fromValue(iterator.key()); + argumentList << QVariant::fromValue(iterator.value()); + m_resolver->asyncCallWithArgumentList(QStringLiteral("SetLinkDomains"), + argumentList); + } + + if (m_ifindex > 0) { + m_resolver->asyncCall(QStringLiteral("RevertLink"), m_ifindex); + } + + logger.debug() << "DnsUtilsLinux destroyed."; +} + +bool DnsUtilsLinux::updateResolvers(const QString& ifname, + const QList& resolvers) { + m_ifindex = if_nametoindex(qPrintable(ifname)); + if (m_ifindex <= 0) { + logger.error() << "Unable to resolve ifindex for" << ifname; + return false; + } + + setLinkDNS(m_ifindex, resolvers); + setLinkDefaultRoute(m_ifindex, true); + updateLinkDomains(); + return true; +} + +bool DnsUtilsLinux::restoreResolvers() { + for (auto iterator = m_linkDomains.constBegin(); + iterator != m_linkDomains.constEnd(); ++iterator) { + setLinkDomains(iterator.key(), iterator.value()); + } + m_linkDomains.clear(); + + /* Revert the VPN interface's DNS configuration */ + if (m_ifindex > 0) { + QList argumentList = {QVariant::fromValue(m_ifindex)}; + QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList( + QStringLiteral("RevertLink"), argumentList); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, + SLOT(dnsCallCompleted(QDBusPendingCallWatcher*))); + + m_ifindex = 0; + } + + return true; +} + +void DnsUtilsLinux::dnsCallCompleted(QDBusPendingCallWatcher* call) { + QDBusPendingReply<> reply = *call; + if (reply.isError()) { + logger.error() << "Error received from the DBus service"; + } + delete call; +} + +void DnsUtilsLinux::setLinkDNS(int ifindex, + const QList& resolvers) { + QList resolverList; + char ifnamebuf[IF_NAMESIZE]; + const char* ifname = if_indextoname(ifindex, ifnamebuf); + for (const auto& ip : resolvers) { + resolverList.append(ip); + if (ifname) { + logger.debug() << "Adding DNS resolver" << ip.toString() << "via" + << ifname; + } + } + + QList argumentList; + argumentList << QVariant::fromValue(ifindex); + argumentList << QVariant::fromValue(resolverList); + QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList( + QStringLiteral("SetLinkDNS"), argumentList); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, + SLOT(dnsCallCompleted(QDBusPendingCallWatcher*))); +} + +void DnsUtilsLinux::setLinkDomains(int ifindex, + const QList& domains) { + char ifnamebuf[IF_NAMESIZE]; + const char* ifname = if_indextoname(ifindex, ifnamebuf); + if (ifname) { + for (const auto& d : domains) { + // The DNS search domains often winds up revealing user's ISP which + // can correlate back to their location. + logger.debug() << "Setting DNS domain:" << logger.sensitive(d.domain) + << "via" << ifname << (d.search ? "search" : ""); + } + } + + QList argumentList; + argumentList << QVariant::fromValue(ifindex); + argumentList << QVariant::fromValue(domains); + QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList( + QStringLiteral("SetLinkDomains"), argumentList); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, + SLOT(dnsCallCompleted(QDBusPendingCallWatcher*))); +} + +void DnsUtilsLinux::setLinkDefaultRoute(int ifindex, bool enable) { + QList argumentList; + argumentList << QVariant::fromValue(ifindex); + argumentList << QVariant::fromValue(enable); + QDBusPendingReply<> reply = m_resolver->asyncCallWithArgumentList( + QStringLiteral("SetLinkDefaultRoute"), argumentList); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, + SLOT(dnsCallCompleted(QDBusPendingCallWatcher*))); +} + +void DnsUtilsLinux::updateLinkDomains() { + /* Get the list of search domains, and remove any others that might conspire + * to satisfy DNS resolution. Unfortunately, this is a pain because Qt doesn't + * seem to be able to demarshall complex property types. + */ + QDBusMessage message = QDBusMessage::createMethodCall( + DBUS_RESOLVE_SERVICE, DBUS_RESOLVE_PATH, DBUS_PROPERTY_INTERFACE, "Get"); + message << QString(DBUS_RESOLVE_MANAGER); + message << QString("Domains"); + QDBusPendingReply reply = + m_resolver->connection().asyncCall(message); + + QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); + QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, + SLOT(dnsDomainsReceived(QDBusPendingCallWatcher*))); +} + +void DnsUtilsLinux::dnsDomainsReceived(QDBusPendingCallWatcher* call) { + QDBusPendingReply reply = *call; + if (reply.isError()) { + logger.error() << "Error retrieving the DNS domains from the DBus service"; + delete call; + return; + } + + /* Update the state of the DNS domains */ + m_linkDomains.clear(); + QDBusArgument args = qvariant_cast(reply.value()); + QList list = qdbus_cast>(args); + for (const auto& d : list) { + if (d.ifindex == 0) { + continue; + } + m_linkDomains[d.ifindex].append(DnsLinkDomain(d.domain, d.search)); + } + + /* Drop any competing root search domains. */ + DnsLinkDomain root = DnsLinkDomain(".", true); + for (auto iterator = m_linkDomains.constBegin(); + iterator != m_linkDomains.constEnd(); ++iterator) { + if (!iterator.value().contains(root)) { + continue; + } + QList newlist = iterator.value(); + newlist.removeAll(root); + setLinkDomains(iterator.key(), newlist); + } + + /* Add a root search domain for the new interface. */ + QList newlist = {root}; + setLinkDomains(m_ifindex, newlist); + delete call; +} + +static DnsMetatypeRegistrationProxy s_dnsMetatypeProxy; diff --git a/client/platforms/linux/daemon/dnsutilslinux.h b/client/platforms/linux/daemon/dnsutilslinux.h new file mode 100644 index 00000000..e4bbd273 --- /dev/null +++ b/client/platforms/linux/daemon/dnsutilslinux.h @@ -0,0 +1,41 @@ +/* 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/. */ + +#ifndef DNSUTILSLINUX_H +#define DNSUTILSLINUX_H + +#include +#include + +#include "daemon/dnsutils.h" +#include "dbustypeslinux.h" + +class DnsUtilsLinux final : public DnsUtils { + Q_OBJECT + Q_DISABLE_COPY_MOVE(DnsUtilsLinux) + + public: + DnsUtilsLinux(QObject* parent); + ~DnsUtilsLinux(); + bool updateResolvers(const QString& ifname, + const QList& resolvers) override; + bool restoreResolvers() override; + + private: + void setLinkDNS(int ifindex, const QList& resolvers); + void setLinkDomains(int ifindex, const QList& domains); + void setLinkDefaultRoute(int ifindex, bool enable); + void updateLinkDomains(); + + private slots: + void dnsCallCompleted(QDBusPendingCallWatcher*); + void dnsDomainsReceived(QDBusPendingCallWatcher*); + + private: + int m_ifindex = 0; + QMap m_linkDomains; + QDBusInterface* m_resolver = nullptr; +}; + +#endif // DNSUTILSLINUX_H diff --git a/client/platforms/linux/daemon/iputilslinux.cpp b/client/platforms/linux/daemon/iputilslinux.cpp new file mode 100644 index 00000000..9a51caad --- /dev/null +++ b/client/platforms/linux/daemon/iputilslinux.cpp @@ -0,0 +1,150 @@ +/* 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 "iputilslinux.h" + +#include +#include +#include +#include + +#include +#include + +#include "daemon/wireguardutils.h" +#include "leakdetector.h" +#include "logger.h" + +constexpr uint32_t ETH_MTU = 1500; +constexpr uint32_t WG_MTU_OVERHEAD = 80; + +namespace { +Logger logger("IPUtilsLinux"); +} + +IPUtilsLinux::IPUtilsLinux(QObject* parent) : IPUtils(parent) { + MZ_COUNT_CTOR(IPUtilsLinux); + logger.debug() << "IPUtilsLinux created."; +} + +IPUtilsLinux::~IPUtilsLinux() { + MZ_COUNT_DTOR(IPUtilsLinux); + logger.debug() << "IPUtilsLinux destroyed."; +} + +bool IPUtilsLinux::addInterfaceIPs(const InterfaceConfig& config) { + return addIP4AddressToDevice(config) && addIP6AddressToDevice(config); +} + +bool IPUtilsLinux::setMTUAndUp(const InterfaceConfig& config) { + Q_UNUSED(config); + + // Create socket file descriptor to perform the ioctl operations on + int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + logger.error() << "Failed to create ioctl socket."; + return false; + } + auto guard = qScopeGuard([&] { close(sockfd); }); + + // Setup the interface to interact with + struct ifreq ifr; + strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + + // MTU + // FIXME: We need to know how many layers deep this particular + // interface is into a tunnel to work effectively. Otherwise + // we will run into fragmentation issues. + ifr.ifr_mtu = ETH_MTU - WG_MTU_OVERHEAD; + int ret = ioctl(sockfd, SIOCSIFMTU, &ifr); + if (ret) { + logger.error() << "Failed to set MTU -- Return code: " << ret; + return false; + } + + // Up + ifr.ifr_flags |= (IFF_UP | IFF_RUNNING); + ret = ioctl(sockfd, SIOCSIFFLAGS, &ifr); + if (ret) { + logger.error() << "Failed to set device up -- Return code: " << ret; + return false; + } + + return true; +} + +bool IPUtilsLinux::addIP4AddressToDevice(const InterfaceConfig& config) { + struct ifreq ifr; + struct sockaddr_in* ifrAddr = (struct sockaddr_in*)&ifr.ifr_addr; + + // Name the interface and set family + strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + + // Get the device address to add to interface + QPair parsedAddr = + QHostAddress::parseSubnet(config.m_deviceIpv4Address); + QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit(); + char* deviceAddr = _deviceAddr.data(); + inet_pton(AF_INET, deviceAddr, &ifrAddr->sin_addr); + + // Create IPv4 socket to perform the ioctl operations on + int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + logger.error() << "Failed to create ioctl socket."; + return false; + } + auto guard = qScopeGuard([&] { close(sockfd); }); + + // Set ifr to interface + int ret = ioctl(sockfd, SIOCSIFADDR, &ifr); + if (ret) { + logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr) + << "error:" << strerror(errno); + return false; + } + return true; +} + +bool IPUtilsLinux::addIP6AddressToDevice(const InterfaceConfig& config) { + // Set up the ifr and the companion ifr6 + struct in6_ifreq ifr6; + ifr6.prefixlen = 64; + + // Get the device address to add to ifr6 interface + QPair parsedAddr = + QHostAddress::parseSubnet(config.m_deviceIpv6Address); + QByteArray _deviceAddr = parsedAddr.first.toString().toLocal8Bit(); + char* deviceAddr = _deviceAddr.data(); + inet_pton(AF_INET6, deviceAddr, &ifr6.addr); + + // Create IPv6 socket to perform the ioctl operations on + int sockfd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP); + if (sockfd < 0) { + logger.error() << "Failed to create ioctl socket."; + return false; + } + auto guard = qScopeGuard([&] { close(sockfd); }); + + // Get the index of named ifr and link with ifr6 + struct ifreq ifr; + strncpy(ifr.ifr_name, WG_INTERFACE, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET6; + int ret = ioctl(sockfd, SIOGIFINDEX, &ifr); + if (ret) { + logger.error() << "Failed to get ifindex. Return code: " << ret; + return false; + } + ifr6.ifindex = ifr.ifr_ifindex; + + // Set ifr6 to the interface + ret = ioctl(sockfd, SIOCSIFADDR, &ifr6); + if (ret && (errno != EEXIST)) { + logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr) + << "error:" << strerror(errno); + return false; + } + + return true; +} diff --git a/client/platforms/linux/daemon/iputilslinux.h b/client/platforms/linux/daemon/iputilslinux.h new file mode 100644 index 00000000..38edf177 --- /dev/null +++ b/client/platforms/linux/daemon/iputilslinux.h @@ -0,0 +1,31 @@ +/* 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/. */ + +#ifndef IPUTILSLINUX_H +#define IPUTILSLINUX_H + +#include + +#include "daemon/iputils.h" + +class IPUtilsLinux final : public IPUtils { + public: + IPUtilsLinux(QObject* parent); + ~IPUtilsLinux(); + bool addInterfaceIPs(const InterfaceConfig& config) override; + bool setMTUAndUp(const InterfaceConfig& config) override; + + private: + bool addIP4AddressToDevice(const InterfaceConfig& config); + bool addIP6AddressToDevice(const InterfaceConfig& config); + + private: + struct in6_ifreq { + struct in6_addr addr; + uint32_t prefixlen; + unsigned int ifindex; + }; +}; + +#endif // IPUTILSLINUX_H \ No newline at end of file diff --git a/client/platforms/linux/daemon/linuxdaemon.cpp b/client/platforms/linux/daemon/linuxdaemon.cpp new file mode 100644 index 00000000..7c2d95db --- /dev/null +++ b/client/platforms/linux/daemon/linuxdaemon.cpp @@ -0,0 +1,52 @@ +/* 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 "linuxdaemon.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leakdetector.h" +#include "logger.h" + +namespace { +Logger logger("LinuxDaemon"); +LinuxDaemon* s_daemon = nullptr; +} // namespace + +LinuxDaemon::LinuxDaemon() : Daemon(nullptr) { + MZ_COUNT_CTOR(LinuxDaemon); + + logger.debug() << "Daemon created"; + + m_wgutils = new WireguardUtilsLinux(this); + m_dnsutils = new DnsUtilsLinux(this); + m_iputils = new IPUtilsLinux(this); + + Q_ASSERT(s_daemon == nullptr); + s_daemon = this; +} + +LinuxDaemon::~LinuxDaemon() { + MZ_COUNT_DTOR(LinuxDaemon); + + logger.debug() << "Daemon released"; + + Q_ASSERT(s_daemon == this); + s_daemon = nullptr; +} + +// static +LinuxDaemon* LinuxDaemon::instance() { + Q_ASSERT(s_daemon); + return s_daemon; +} diff --git a/client/platforms/linux/daemon/linuxdaemon.h b/client/platforms/linux/daemon/linuxdaemon.h new file mode 100644 index 00000000..7f5d27b7 --- /dev/null +++ b/client/platforms/linux/daemon/linuxdaemon.h @@ -0,0 +1,36 @@ +/* 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/. */ + +#ifndef LINUXDAEMON_H +#define LINUXDAEMON_H + + +#include "daemon/daemon.h" +#include "dnsutilslinux.h" +#include "iputilslinux.h" +#include "wireguardutilslinux.h" + +class LinuxDaemon final : public Daemon { + friend class IPUtilsMacos; + + public: + LinuxDaemon(); + ~LinuxDaemon(); + + static LinuxDaemon* instance(); + + protected: + WireguardUtils* wgutils() const override { return m_wgutils; } + bool supportDnsUtils() const override { return true; } + DnsUtils* dnsutils() override { return m_dnsutils; } + bool supportIPUtils() const override { return true; } + IPUtils* iputils() override { return m_iputils; } + + private: + WireguardUtilsLinux* m_wgutils = nullptr; + DnsUtilsLinux* m_dnsutils = nullptr; + IPUtilsLinux* m_iputils = nullptr; +}; + +#endif // LINUXDAEMON_H diff --git a/client/platforms/linux/daemon/linuxroutemonitor.cpp b/client/platforms/linux/daemon/linuxroutemonitor.cpp new file mode 100644 index 00000000..80f510b7 --- /dev/null +++ b/client/platforms/linux/daemon/linuxroutemonitor.cpp @@ -0,0 +1,133 @@ +/* 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 "linuxroutemonitor.h" + +#include "router_linux.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "leakdetector.h" +#include "logger.h" + +namespace { +Logger logger("LinuxRouteMonitor"); +} // namespace + +LinuxRouteMonitor::LinuxRouteMonitor(const QString& ifname, QObject* parent) + : QObject(parent), m_ifname(ifname) { + MZ_COUNT_CTOR(LinuxRouteMonitor); + logger.debug() << "LinuxRouteMonitor created."; + + m_rtsock = socket(PF_ROUTE, SOCK_RAW, 0); + if (m_rtsock < 0) { + logger.error() << "Failed to create routing socket:" << strerror(errno); + return; + } + + RouterLinux &router = RouterLinux::Instance(); + m_defaultGatewayIpv4 = router.getgatewayandiface().toUtf8(); + + m_ifindex = if_nametoindex(qPrintable(ifname)); + m_notifier = new QSocketNotifier(m_rtsock, QSocketNotifier::Read, this); +} + +LinuxRouteMonitor::~LinuxRouteMonitor() { + MZ_COUNT_DTOR(LinuxRouteMonitor); + flushExclusionRoutes(); + if (m_rtsock >= 0) { + close(m_rtsock); + } + logger.debug() << "LinuxRouteMonitor destroyed."; +} + +// Compare memory against zero. +static int memcmpzero(const void* data, size_t len) { + const quint8* ptr = static_cast(data); + while (len--) { + if (*ptr++) return 1; + } + return 0; +} + +bool LinuxRouteMonitor::insertRoute(const IPAddress& prefix) { + int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + struct ifreq ifc; + int res; + + if(temp_sock < 0) + return -1; + strcpy(ifc.ifr_name, m_ifname.toUtf8()); + + res = ioctl(temp_sock, SIOCGIFADDR, &ifc); + if(res < 0) + return -1; + + RouterLinux &router = RouterLinux::Instance(); + logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); + return router.routeAdd(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); +} + +bool LinuxRouteMonitor::deleteRoute(const IPAddress& prefix) { + int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + + struct ifreq ifc; + int res; + if(temp_sock < 0) + temp_sock -1; + strcpy(ifc.ifr_name, m_ifname.toUtf8()); + + res = ioctl(temp_sock, SIOCGIFADDR, &ifc); + if(res < 0) + return -1; + + RouterLinux &router = RouterLinux::Instance(); + logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); + return router.routeDelete(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); +} + +bool LinuxRouteMonitor::addExclusionRoute(const IPAddress& prefix) { + logger.debug() << "Adding exclusion route for" + << logger.sensitive(prefix.toString()); + + int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + RouterLinux &router = RouterLinux::Instance(); + logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; + return router.routeAdd(prefix.toString(), m_defaultGatewayIpv4, temp_sock); + // Otherwise, the default route isn't known yet. Do nothing. + return true; +} + +bool LinuxRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { + logger.debug() << "Deleting exclusion route for" + << logger.sensitive(prefix.toString()); + + int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + RouterLinux &router = RouterLinux::Instance(); + logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; + return router.routeDelete(prefix.toString(), m_defaultGatewayIpv4, temp_sock); +} + +void LinuxRouteMonitor::flushExclusionRoutes() { + RouterLinux &router = RouterLinux::Instance(); + router.clearSavedRoutes(); +} diff --git a/client/platforms/linux/daemon/linuxroutemonitor.h b/client/platforms/linux/daemon/linuxroutemonitor.h new file mode 100644 index 00000000..872fcb7e --- /dev/null +++ b/client/platforms/linux/daemon/linuxroutemonitor.h @@ -0,0 +1,53 @@ +/* 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/. */ + +#ifndef LINUXROUTEMONITOR_H +#define LINUXROUTEMONITOR_H + +#include +#include +#include +#include +#include + +#include "ipaddress.h" + +struct if_msghdr; +struct rt_msghdr; +struct sockaddr; + +class LinuxRouteMonitor final : public QObject { + Q_OBJECT + + public: + LinuxRouteMonitor(const QString& ifname, QObject* parent = nullptr); + ~LinuxRouteMonitor(); + + bool insertRoute(const IPAddress& prefix); + bool deleteRoute(const IPAddress& prefix); + int interfaceFlags() { return m_ifflags; } + + bool addExclusionRoute(const IPAddress& prefix); + bool deleteExclusionRoute(const IPAddress& prefix); + void flushExclusionRoutes(); + + private: + static QString addrToString(const struct sockaddr* sa); + static QString addrToString(const QByteArray& data); + + QList m_exclusionRoutes; + QByteArray m_defaultGatewayIpv4; + QByteArray m_defaultGatewayIpv6; + unsigned int m_defaultIfindexIpv4 = 0; + unsigned int m_defaultIfindexIpv6 = 0; + + QString m_ifname; + unsigned int m_ifindex = 0; + int m_ifflags = 0; + int m_rtsock = -1; + int m_rtseq = 0; + QSocketNotifier* m_notifier = nullptr; +}; + +#endif // LINUXROUTEMONITOR_H diff --git a/client/platforms/linux/daemon/pidtracker.cpp b/client/platforms/linux/daemon/pidtracker.cpp new file mode 100644 index 00000000..76d53219 --- /dev/null +++ b/client/platforms/linux/daemon/pidtracker.cpp @@ -0,0 +1,228 @@ +/* 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 "pidtracker.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "leakdetector.h" +#include "logger.h" + +constexpr size_t CN_MCAST_MSG_SIZE = + sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op); + +namespace { +Logger logger("PidTracker"); +} + +PidTracker::PidTracker(QObject* parent) : QObject(parent) { + MZ_COUNT_CTOR(PidTracker); + logger.debug() << "PidTracker created."; + + m_nlsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (m_nlsock < 0) { + logger.error() << "Failed to create netlink socket:" << strerror(errno); + return; + } + + struct sockaddr_nl nladdr; + nladdr.nl_family = AF_NETLINK; + nladdr.nl_groups = CN_IDX_PROC; + nladdr.nl_pid = getpid(); + nladdr.nl_pad = 0; + if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) { + logger.error() << "Failed to bind netlink socket:" << strerror(errno); + close(m_nlsock); + m_nlsock = -1; + return; + } + + char buf[NLMSG_SPACE(CN_MCAST_MSG_SIZE)]; + struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; + struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(nlmsg); + enum proc_cn_mcast_op mcast_op = PROC_CN_MCAST_LISTEN; + + memset(buf, 0, sizeof(buf)); + nlmsg->nlmsg_len = NLMSG_LENGTH(CN_MCAST_MSG_SIZE); + nlmsg->nlmsg_type = NLMSG_DONE; + nlmsg->nlmsg_flags = 0; + nlmsg->nlmsg_seq = 0; + nlmsg->nlmsg_pid = getpid(); + + cnmsg->id.idx = CN_IDX_PROC; + cnmsg->id.val = CN_VAL_PROC; + cnmsg->seq = 0; + cnmsg->ack = 0; + cnmsg->len = sizeof(mcast_op); + memcpy(cnmsg->data, &mcast_op, sizeof(mcast_op)); + + if (send(m_nlsock, nlmsg, sizeof(buf), 0) != sizeof(buf)) { + logger.error() << "Failed to send netlink message:" << strerror(errno); + close(m_nlsock); + m_nlsock = -1; + return; + } + + m_socket = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); + connect(m_socket, &QSocketNotifier::activated, this, &PidTracker::readData); +} + +PidTracker::~PidTracker() { + MZ_COUNT_DTOR(PidTracker); + logger.debug() << "PidTracker destroyed."; + + m_processTree.clear(); + while (!m_processGroups.isEmpty()) { + ProcessGroup* group = m_processGroups.takeFirst(); + delete group; + } + + if (m_nlsock > 0) { + close(m_nlsock); + } +} + +ProcessGroup* PidTracker::track(const QString& name, int rootpid) { + ProcessGroup* group = m_processTree.value(rootpid, nullptr); + if (group) { + logger.warning() << "Ignoring attempt to track duplicate PID"; + return group; + } + group = new ProcessGroup(name, rootpid); + group->kthreads[rootpid] = 1; + group->refcount = 1; + + m_processGroups.append(group); + m_processTree[rootpid] = group; + + return group; +} + +void PidTracker::handleProcEvent(struct cn_msg* cnmsg) { + struct proc_event* ev = (struct proc_event*)cnmsg->data; + + if (ev->what == proc_event::PROC_EVENT_FORK) { + auto forkdata = &ev->event_data.fork; + /* If the child process already exists, track a new kernel thread. */ + ProcessGroup* group = m_processTree.value(forkdata->child_tgid, nullptr); + if (group) { + group->kthreads[forkdata->child_tgid]++; + return; + } + + /* Track a new userspace process if was forked from a known parent. */ + group = m_processTree.value(forkdata->parent_tgid, nullptr); + if (!group) { + return; + } + m_processTree[forkdata->child_tgid] = group; + group->kthreads[forkdata->child_tgid] = 1; + group->refcount++; + emit pidForked(group->name, forkdata->parent_tgid, forkdata->child_tgid); + } + + if (ev->what == proc_event::PROC_EVENT_EXIT) { + auto exitdata = &ev->event_data.exit; + ProcessGroup* group = m_processTree.value(exitdata->process_tgid, nullptr); + if (!group) { + return; + } + + /* Decrement the number of kernel threads in this userspace process. */ + uint threadcount = group->kthreads.value(exitdata->process_tgid, 0); + if (threadcount == 0) { + return; + } + if (threadcount > 1) { + group->kthreads[exitdata->process_tgid] = threadcount - 1; + return; + } + group->kthreads.remove(exitdata->process_tgid); + + /* A userspace process exits when all of its kernel threads exit. */ + Q_ASSERT(group->refcount > 0); + group->refcount--; + if (group->refcount == 0) { + emit terminated(group->name, group->rootpid); + m_processGroups.removeAll(group); + delete group; + } + } +} + +void PidTracker::readData() { + struct sockaddr_nl src; + socklen_t srclen = sizeof(src); + ssize_t recvlen; + + recvlen = recvfrom(m_nlsock, m_readBuf, sizeof(m_readBuf), MSG_DONTWAIT, + (struct sockaddr*)&src, &srclen); + if (recvlen == ENOBUFS) { + logger.error() + << "Failed to read netlink socket: buffer full, message dropped"; + return; + } + if (recvlen < 0) { + logger.error() << "Failed to read netlink socket:" << strerror(errno); + return; + } + if (srclen != sizeof(src)) { + logger.error() << "Failed to read netlink socket: invalid address length"; + return; + } + + /* We are only interested in process-control messages from the kernel */ + if ((src.nl_groups != CN_IDX_PROC) || (src.nl_pid != 0)) { + return; + } + + /* Handle the process-control messages. */ + struct nlmsghdr* msg; + for (msg = (struct nlmsghdr*)m_readBuf; NLMSG_OK(msg, recvlen); + msg = NLMSG_NEXT(msg, recvlen)) { + struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(msg); + if (msg->nlmsg_type == NLMSG_NOOP) { + continue; + } + if ((msg->nlmsg_type == NLMSG_ERROR) || + (msg->nlmsg_type == NLMSG_OVERRUN)) { + break; + } + handleProcEvent(cnmsg); + if (msg->nlmsg_type == NLMSG_DONE) { + break; + } + } +} + +bool ProcessGroup::moveToCgroup(const QString& name) { + /* Do nothing if Cgroups are not supported. */ + if (name.isNull()) { + return true; + } + + QString cgProcsFile = name + "/cgroup.procs"; + FILE* fp = fopen(qPrintable(cgProcsFile), "w"); + if (!fp) { + return false; + } + + for (auto iterator = kthreads.constBegin(); iterator != kthreads.constEnd(); + ++iterator) { + fprintf(fp, "%d\n", iterator.key()); + fflush(fp); + } + fclose(fp); + return true; +} diff --git a/client/platforms/linux/daemon/pidtracker.h b/client/platforms/linux/daemon/pidtracker.h new file mode 100644 index 00000000..dc632b8b --- /dev/null +++ b/client/platforms/linux/daemon/pidtracker.h @@ -0,0 +1,72 @@ +/* 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/. */ + +#ifndef PIDTRACKER_H +#define PIDTRACKER_H + +#include +#include +#include +#include + +#include "leakdetector.h" + +struct cn_msg; + +class ProcessGroup { + public: + ProcessGroup(const QString& groupName, int groupRootPid, + const QString& groupState = "active") { + MZ_COUNT_CTOR(ProcessGroup); + name = groupName; + rootpid = groupRootPid; + state = groupState; + refcount = 0; + } + ~ProcessGroup() { MZ_COUNT_DTOR(ProcessGroup); } + + bool moveToCgroup(const QString& name); + + QHash kthreads; + QString name; + QString state; + int rootpid; + int refcount; +}; + +class PidTracker final : public QObject { + Q_OBJECT + Q_DISABLE_COPY_MOVE(PidTracker) + + public: + explicit PidTracker(QObject* parent); + ~PidTracker(); + + ProcessGroup* track(const QString& name, int rootpid); + + QList pids() { return m_processTree.keys(); } + QList::iterator begin() { return m_processGroups.begin(); } + QList::iterator end() { return m_processGroups.end(); } + ProcessGroup* group(int pid) { return m_processTree.value(pid); } + + signals: + void pidForked(const QString& name, int parent, int child); + void pidExited(const QString& name, int pid); + void terminated(const QString& name, int rootpid); + + private: + void handleProcEvent(struct cn_msg*); + + private slots: + void readData(); + + private: + int m_nlsock; + char m_readBuf[2048]; + QSocketNotifier* m_socket = nullptr; + QHash m_processTree; + QList m_processGroups; +}; + +#endif // PIDTRACKER_H diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp new file mode 100644 index 00000000..32150ad5 --- /dev/null +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -0,0 +1,369 @@ +/* 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 "wireguardutilslinux.h" + +#include + +#include +#include +#include +#include +#include + +#include "leakdetector.h" +#include "logger.h" + +constexpr const int WG_TUN_PROC_TIMEOUT = 5000; +constexpr const char* WG_RUNTIME_DIR = "/var/run/wireguard"; + +namespace { +Logger logger("WireguardUtilsLinux"); +Logger logwireguard("WireguardGo"); +}; // namespace + +WireguardUtilsLinux::WireguardUtilsLinux(QObject* parent) + : WireguardUtils(parent), m_tunnel(this) { + MZ_COUNT_CTOR(WireguardUtilsLinux); + logger.debug() << "WireguardUtilsLinux created."; + + connect(&m_tunnel, SIGNAL(readyReadStandardOutput()), this, + SLOT(tunnelStdoutReady())); + connect(&m_tunnel, SIGNAL(errorOccurred(QProcess::ProcessError)), this, + SLOT(tunnelErrorOccurred(QProcess::ProcessError))); +} + +WireguardUtilsLinux::~WireguardUtilsLinux() { + MZ_COUNT_DTOR(WireguardUtilsLinux); + logger.debug() << "WireguardUtilsLinux destroyed."; +} + +void WireguardUtilsLinux::tunnelStdoutReady() { + for (;;) { + QByteArray line = m_tunnel.readLine(); + if (line.length() <= 0) { + break; + } + logwireguard.debug() << QString::fromUtf8(line); + } +} + +void WireguardUtilsLinux::tunnelErrorOccurred(QProcess::ProcessError error) { + logger.warning() << "Tunnel process encountered an error:" << error; + emit backendFailure(); +} + +bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { + Q_UNUSED(config); + if (m_tunnel.state() != QProcess::NotRunning) { + logger.warning() << "Unable to start: tunnel process already running"; + return false; + } + + QDir wgRuntimeDir(WG_RUNTIME_DIR); + if (!wgRuntimeDir.exists()) { + wgRuntimeDir.mkpath("."); + } + + QProcessEnvironment pe = QProcessEnvironment::systemEnvironment(); + QString wgNameFile = wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".sock"); + pe.insert("WG_TUN_NAME_FILE", wgNameFile); +#ifdef MZ_DEBUG + pe.insert("LOG_LEVEL", "debug"); +#endif + m_tunnel.setProcessEnvironment(pe); + + QDir appPath(QCoreApplication::applicationDirPath()); + QStringList wgArgs = {"-f", "amn0"}; + m_tunnel.start(appPath.filePath("wireguard-go"), wgArgs); + if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) { + logger.error() << "Unable to start tunnel process due to timeout"; + m_tunnel.kill(); + return false; + } + + m_ifname = waitForTunnelName(wgNameFile); + if (m_ifname.isNull()) { + logger.error() << "Unable to read tunnel interface name"; + m_tunnel.kill(); + return false; + } + logger.debug() << "Created wireguard interface" << m_ifname; + + // Start the routing table monitor. + m_rtmonitor = new LinuxRouteMonitor(m_ifname, this); + + // Send a UAPI command to configure the interface + QString message("set=1\n"); + QByteArray privateKey = QByteArray::fromBase64(config.m_privateKey.toUtf8()); + QTextStream out(&message); + out << "private_key=" << QString(privateKey.toHex()) << "\n"; + out << "replace_peers=true\n"; + int err = uapiErrno(uapiCommand(message)); + if (err != 0) { + logger.error() << "Interface configuration failed:" << strerror(err); + } + return (err == 0); +} + +bool WireguardUtilsLinux::deleteInterface() { + if (m_rtmonitor) { + delete m_rtmonitor; + m_rtmonitor = nullptr; + } + + if (m_tunnel.state() == QProcess::NotRunning) { + return false; + } + + // Attempt to terminate gracefully. + m_tunnel.terminate(); + if (!m_tunnel.waitForFinished(WG_TUN_PROC_TIMEOUT)) { + m_tunnel.kill(); + m_tunnel.waitForFinished(WG_TUN_PROC_TIMEOUT); + } + + // Garbage collect. + QDir wgRuntimeDir(WG_RUNTIME_DIR); + QFile::remove(wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name")); + return true; +} + +// dummy implementations for now +bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) { + QByteArray publicKey = + QByteArray::fromBase64(qPrintable(config.m_serverPublicKey)); + + QByteArray pskKey = QByteArray::fromBase64(qPrintable(config.m_serverPskKey)); + + logger.debug() << "Configuring peer" << config.m_serverPublicKey << "via" << config.m_serverIpv4AddrIn; + + // Update/create the peer config + QString message; + QTextStream out(&message); + out << "set=1\n"; + out << "public_key=" << QString(publicKey.toHex()) << "\n"; + out << "preshared_key=" << QString(pskKey.toHex()) << "\n"; + if (!config.m_serverIpv4AddrIn.isNull()) { + out << "endpoint=" << config.m_serverIpv4AddrIn << ":"; + } else if (!config.m_serverIpv6AddrIn.isNull()) { + out << "endpoint=[" << config.m_serverIpv6AddrIn << "]:"; + } else { + logger.warning() << "Failed to create peer with no endpoints"; + return false; + } + out << config.m_serverPort << "\n"; + + out << "replace_allowed_ips=true\n"; + out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n"; + for (const IPAddress& ip : config.m_allowedIPAddressRanges) { + out << "allowed_ip=" << ip.toString() << "\n"; + } + + // Exclude the server address, except for multihop exit servers. + if ((config.m_hopType != InterfaceConfig::MultiHopExit) && + (m_rtmonitor != nullptr)) { + m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); + m_rtmonitor->addExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); + } + + int err = uapiErrno(uapiCommand(message)); + if (err != 0) { + logger.error() << "Peer configuration failed:" << strerror(err); + } + return (err == 0); +} + +bool WireguardUtilsLinux::deletePeer(const InterfaceConfig& config) { + QByteArray publicKey = + QByteArray::fromBase64(qPrintable(config.m_serverPublicKey)); + + // Clear exclustion routes for this peer. + if ((config.m_hopType != InterfaceConfig::MultiHopExit) && + (m_rtmonitor != nullptr)) { + m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv4AddrIn)); + m_rtmonitor->deleteExclusionRoute(IPAddress(config.m_serverIpv6AddrIn)); + } + + QString message; + QTextStream out(&message); + out << "set=1\n"; + out << "public_key=" << QString(publicKey.toHex()) << "\n"; + out << "remove=true\n"; + + int err = uapiErrno(uapiCommand(message)); + if (err != 0) { + logger.error() << "Peer deletion failed:" << strerror(err); + } + return (err == 0); +} + +QList WireguardUtilsLinux::getPeerStatus() { + QString reply = uapiCommand("get=1"); + PeerStatus status; + QList peerList; + for (const QString& line : reply.split('\n')) { + int eq = line.indexOf('='); + if (eq <= 0) { + continue; + } + QString name = line.left(eq); + QString value = line.mid(eq + 1); + + if (name == "public_key") { + if (!status.m_pubkey.isEmpty()) { + peerList.append(status); + } + QByteArray pubkey = QByteArray::fromHex(value.toUtf8()); + status = PeerStatus(pubkey.toBase64()); + } + + if (name == "tx_bytes") { + status.m_txBytes = value.toDouble(); + } + if (name == "rx_bytes") { + status.m_rxBytes = value.toDouble(); + } + if (name == "last_handshake_time_sec") { + status.m_handshake += value.toLongLong() * 1000; + } + if (name == "last_handshake_time_nsec") { + status.m_handshake += value.toLongLong() / 1000000; + } + } + if (!status.m_pubkey.isEmpty()) { + peerList.append(status); + } + + return peerList; +} + +bool WireguardUtilsLinux::updateRoutePrefix(const IPAddress& prefix) { + if (!m_rtmonitor) { + return false; + } + if (prefix.prefixLength() > 0) { + return m_rtmonitor->insertRoute(prefix); + } + + // Ensure that we do not replace the default route. + if (prefix.type() == QAbstractSocket::IPv4Protocol) { + return m_rtmonitor->insertRoute(IPAddress("0.0.0.0/1")) && + m_rtmonitor->insertRoute(IPAddress("128.0.0.0/1")); + } + if (prefix.type() == QAbstractSocket::IPv6Protocol) { + return m_rtmonitor->insertRoute(IPAddress("::/1")) && + m_rtmonitor->insertRoute(IPAddress("8000::/1")); + } + + return false; +} + +bool WireguardUtilsLinux::deleteRoutePrefix(const IPAddress& prefix) { + if (!m_rtmonitor) { + return false; + } + if (prefix.prefixLength() > 0) { + return m_rtmonitor->insertRoute(prefix); + } + + // Ensure that we do not replace the default route. + if (prefix.type() == QAbstractSocket::IPv4Protocol) { + return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) && + m_rtmonitor->deleteRoute(IPAddress("128.0.0.0/1")); + } else if (prefix.type() == QAbstractSocket::IPv6Protocol) { + return m_rtmonitor->deleteRoute(IPAddress("::/1")) && + m_rtmonitor->deleteRoute(IPAddress("8000::/1")); + } else { + return false; + } +} + +bool WireguardUtilsLinux::addExclusionRoute(const IPAddress& prefix) { + if (!m_rtmonitor) { + return false; + } + return m_rtmonitor->addExclusionRoute(prefix); +} + +bool WireguardUtilsLinux::deleteExclusionRoute(const IPAddress& prefix) { + if (!m_rtmonitor) { + return false; + } + return m_rtmonitor->deleteExclusionRoute(prefix); +} + +QString WireguardUtilsLinux::uapiCommand(const QString& command) { + QLocalSocket socket; + QTimer uapiTimeout; + QDir wgRuntimeDir(WG_RUNTIME_DIR); + QString wgSocketFile = wgRuntimeDir.filePath(m_ifname + ".sock"); + + uapiTimeout.setSingleShot(true); + uapiTimeout.start(WG_TUN_PROC_TIMEOUT); + + socket.connectToServer(wgSocketFile, QIODevice::ReadWrite); + if (!socket.waitForConnected(WG_TUN_PROC_TIMEOUT)) { + logger.error() << "QLocalSocket::waitForConnected() failed:" + << socket.errorString(); + return QString(); + } + + // Send the message to the UAPI socket. + QByteArray message = command.toLocal8Bit(); + while (!message.endsWith("\n\n")) { + message.append('\n'); + } + socket.write(message); + + QByteArray reply; + while (!reply.contains("\n\n")) { + if (!uapiTimeout.isActive()) { + logger.error() << "UAPI command timed out"; + return QString(); + } + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + reply.append(socket.readAll()); + } + + return QString::fromUtf8(reply).trimmed(); +} + +// static +int WireguardUtilsLinux::uapiErrno(const QString& reply) { + for (const QString& line : reply.split("\n")) { + int eq = line.indexOf('='); + if (eq <= 0) { + continue; + } + if (line.left(eq) == "errno") { + return line.mid(eq + 1).toInt(); + } + } + return EINVAL; +} + +QString WireguardUtilsLinux::waitForTunnelName(const QString& filename) { + QTimer timeout; + timeout.setSingleShot(true); + timeout.start(WG_TUN_PROC_TIMEOUT); + + QFile file(filename); + + while ((m_tunnel.state() == QProcess::Running) && timeout.isActive()) { + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + QString ifname = "amn0"; + + // Test-connect to the UAPI socket. + QLocalSocket sock; + QDir wgRuntimeDir(WG_RUNTIME_DIR); + QString sockName = wgRuntimeDir.filePath(ifname + ".sock"); + sock.connectToServer(sockName, QIODevice::ReadWrite); + if (sock.waitForConnected(100)) { + return ifname; + } + } + + return QString(); +} diff --git a/client/platforms/linux/daemon/wireguardutilslinux.h b/client/platforms/linux/daemon/wireguardutilslinux.h new file mode 100644 index 00000000..a8320c95 --- /dev/null +++ b/client/platforms/linux/daemon/wireguardutilslinux.h @@ -0,0 +1,55 @@ +/* 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/. */ + +#ifndef WIREGUARDUTILSLINUX_H +#define WIREGUARDUTILSLINUX_H + +#include +#include + +#include "daemon/wireguardutils.h" +#include "linuxroutemonitor.h" + +class WireguardUtilsLinux final : public WireguardUtils { + Q_OBJECT + +public: + WireguardUtilsLinux(QObject* parent); + ~WireguardUtilsLinux(); + + bool interfaceExists() override { + return m_tunnel.state() == QProcess::Running; + } + QString interfaceName() override { return m_ifname; } + bool addInterface(const InterfaceConfig& config) override; + bool deleteInterface() override; + + bool updatePeer(const InterfaceConfig& config) override; + bool deletePeer(const InterfaceConfig& config) override; + QList getPeerStatus() override; + + bool updateRoutePrefix(const IPAddress& prefix) override; + bool deleteRoutePrefix(const IPAddress& prefix) override; + + bool addExclusionRoute(const IPAddress& prefix) override; + bool deleteExclusionRoute(const IPAddress& prefix) override; + +signals: + void backendFailure(); + +private slots: + void tunnelStdoutReady(); + void tunnelErrorOccurred(QProcess::ProcessError error); + +private: + QString uapiCommand(const QString& command); + static int uapiErrno(const QString& command); + QString waitForTunnelName(const QString& filename); + + QString m_ifname; + QProcess m_tunnel; + LinuxRouteMonitor* m_rtmonitor = nullptr; +}; + +#endif // WIREGUARDUTILSLINUX_H diff --git a/client/platforms/linux/interfaceconfig.h b/client/platforms/linux/interfaceconfig.h new file mode 100644 index 00000000..bd4e383a --- /dev/null +++ b/client/platforms/linux/interfaceconfig.h @@ -0,0 +1,47 @@ +/* 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/. */ + +#ifndef INTERFACECONFIG_H +#define INTERFACECONFIG_H + +#include +#include + +#include "ipaddress.h" + +class QJsonObject; + +class InterfaceConfig { + Q_GADGET + + public: + InterfaceConfig() {} + + enum HopType { SingleHop, MultiHopEntry, MultiHopExit }; + Q_ENUM(HopType) + + HopType m_hopType; + QString m_privateKey; + QString m_deviceIpv4Address; + QString m_deviceIpv6Address; + QString m_serverIpv4Gateway; + QString m_serverIpv6Gateway; + QString m_serverPublicKey; + QString m_serverIpv4AddrIn; + QString m_serverIpv6AddrIn; + QString m_dnsServer; + int m_serverPort = 0; + QList m_allowedIPAddressRanges; + QStringList m_excludedAddresses; + QStringList m_vpnDisabledApps; +#if defined(MZ_ANDROID) || defined(MZ_IOS) + QString m_installationId; +#endif + + QJsonObject toJson() const; + QString toWgConf( + const QMap& extra = QMap()) const; +}; + +#endif // INTERFACECONFIG_H diff --git a/client/platforms/linux/linuxdependencies.cpp b/client/platforms/linux/linuxdependencies.cpp new file mode 100644 index 00000000..3a4cb259 --- /dev/null +++ b/client/platforms/linux/linuxdependencies.cpp @@ -0,0 +1,139 @@ +/* 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 "linuxdependencies.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +//#include "dbusclient.h" +#include "logger.h" + +namespace { + +Logger logger("LinuxDependencies"); + +void showAlert(const QString& message) { + logger.debug() << "Show alert:" << message; + + QMessageBox alert; + alert.setText(message); + alert.exec(); +} + +bool checkDaemonVersion() { + logger.debug() << "Check Daemon Version"; + + bool completed = false; + bool value = false; + + while (!completed) { + QCoreApplication::processEvents(); + } + + return value; +} + +} // namespace + +// static +bool LinuxDependencies::checkDependencies() { + char* path = getenv("PATH"); + if (!path) { + showAlert("No PATH env found."); + return false; + } + + if (!checkDaemonVersion()) { + showAlert("mozillavpn linuxdaemon needs to be updated or restarted."); + return false; + } + + return true; +} + +// static +QString LinuxDependencies::findCgroupPath(const QString& type) { + struct mntent entry; + char buf[PATH_MAX]; + + FILE* fp = fopen("/etc/mtab", "r"); + if (fp == NULL) { + return QString(); + } + + while (getmntent_r(fp, &entry, buf, sizeof(buf)) != NULL) { + if (strcmp(entry.mnt_type, "cgroup") != 0) { + continue; + } + if (hasmntopt(&entry, type.toLocal8Bit().constData()) != NULL) { + fclose(fp); + return QString(entry.mnt_dir); + } + } + fclose(fp); + + return QString(); +} + +// static +QString LinuxDependencies::findCgroup2Path() { + struct mntent entry; + char buf[PATH_MAX]; + + FILE* fp = fopen("/etc/mtab", "r"); + if (fp == NULL) { + return QString(); + } + + while (getmntent_r(fp, &entry, buf, sizeof(buf)) != NULL) { + if (strcmp(entry.mnt_type, "cgroup2") != 0) { + continue; + } + return QString(entry.mnt_dir); + } + fclose(fp); + + return QString(); +} + +// static +QString LinuxDependencies::gnomeShellVersion() { + QDBusInterface iface("org.gnome.Shell", "/org/gnome/Shell", + "org.gnome.Shell"); + if (!iface.isValid()) { + return QString(); + } + + QVariant shellVersion = iface.property("ShellVersion"); + if (!shellVersion.isValid()) { + return QString(); + } + return shellVersion.toString(); +} + +// static +QString LinuxDependencies::kdeFrameworkVersion() { + QProcess proc; + proc.start("kf5-config", QStringList{"--version"}, QIODeviceBase::ReadOnly); + if (!proc.waitForFinished()) { + return QString(); + } + + QByteArray result = proc.readAllStandardOutput(); + for (const QByteArray& line : result.split('\n')) { + if (line.startsWith("KDE Frameworks: ")) { + return QString::fromUtf8(line.last(line.size() - 16)); + } + } + + return QString(); +} diff --git a/client/platforms/linux/linuxdependencies.h b/client/platforms/linux/linuxdependencies.h new file mode 100644 index 00000000..70ff6f18 --- /dev/null +++ b/client/platforms/linux/linuxdependencies.h @@ -0,0 +1,25 @@ +/* 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/. */ + +#ifndef LINUXDEPENDENCIES_H +#define LINUXDEPENDENCIES_H + +#include + +class LinuxDependencies final { + public: + static bool checkDependencies(); + static QString findCgroupPath(const QString& type); + static QString findCgroup2Path(); + static QString gnomeShellVersion(); + static QString kdeFrameworkVersion(); + + private: + LinuxDependencies() = default; + ~LinuxDependencies() = default; + + Q_DISABLE_COPY(LinuxDependencies) +}; + +#endif // LINUXDEPENDENCIES_H diff --git a/client/platforms/linux/linuxnetworkwatcher.cpp b/client/platforms/linux/linuxnetworkwatcher.cpp new file mode 100644 index 00000000..c8ae0fea --- /dev/null +++ b/client/platforms/linux/linuxnetworkwatcher.cpp @@ -0,0 +1,57 @@ +/* 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 "linuxnetworkwatcher.h" + +#include + +#include "leakdetector.h" +#include "linuxnetworkwatcherworker.h" +#include "logger.h" + +namespace { +Logger logger("LinuxNetworkWatcher"); +} + +LinuxNetworkWatcher::LinuxNetworkWatcher(QObject* parent) + : NetworkWatcherImpl(parent) { + MZ_COUNT_CTOR(LinuxNetworkWatcher); + + m_thread.start(); +} + +LinuxNetworkWatcher::~LinuxNetworkWatcher() { + MZ_COUNT_DTOR(LinuxNetworkWatcher); + + delete m_worker; + + m_thread.quit(); + m_thread.wait(); +} + +void LinuxNetworkWatcher::initialize() { + logger.debug() << "initialize"; + + m_worker = new LinuxNetworkWatcherWorker(&m_thread); + + connect(this, &LinuxNetworkWatcher::checkDevicesInThread, m_worker, + &LinuxNetworkWatcherWorker::checkDevices); + + connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this, + &LinuxNetworkWatcher::unsecuredNetwork); + + // Let's wait a few seconds to allow the UI to be fully loaded and shown. + // This is not strictly needed, but it's better for user experience because + // it makes the UI faster to appear, plus it gives a bit of delay between the + // UI to appear and the first notification. + QTimer::singleShot(2000, this, [this]() { + QMetaObject::invokeMethod(m_worker, "initialize", Qt::QueuedConnection); + }); +} + +void LinuxNetworkWatcher::start() { + logger.debug() << "actived"; + NetworkWatcherImpl::start(); + emit checkDevicesInThread(); +} diff --git a/client/platforms/linux/linuxnetworkwatcher.h b/client/platforms/linux/linuxnetworkwatcher.h new file mode 100644 index 00000000..ed8c76ba --- /dev/null +++ b/client/platforms/linux/linuxnetworkwatcher.h @@ -0,0 +1,38 @@ +/* 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/. */ + +#ifndef LINUXNETWORKWATCHER_H +#define LINUXNETWORKWATCHER_H + +#include + +#include "networkwatcherimpl.h" + +class LinuxNetworkWatcherWorker; + +class LinuxNetworkWatcher final : public NetworkWatcherImpl { + Q_OBJECT + + public: + explicit LinuxNetworkWatcher(QObject* parent); + ~LinuxNetworkWatcher(); + + void initialize() override; + + void start() override; + + NetworkWatcherImpl::TransportType getTransportType() { + // TODO: Find out how to do that on linux generally. (VPN-2382) + return NetworkWatcherImpl::TransportType_Unknown; + }; + + signals: + void checkDevicesInThread(); + + private: + LinuxNetworkWatcherWorker* m_worker = nullptr; + QThread m_thread; +}; + +#endif // LINUXNETWORKWATCHER_H diff --git a/client/platforms/linux/linuxnetworkwatcherworker.cpp b/client/platforms/linux/linuxnetworkwatcherworker.cpp new file mode 100644 index 00000000..19ed3251 --- /dev/null +++ b/client/platforms/linux/linuxnetworkwatcherworker.cpp @@ -0,0 +1,177 @@ +/* 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 "linuxnetworkwatcherworker.h" + +#include + +#include "leakdetector.h" +#include "logger.h" + +// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceType +#ifndef NM_DEVICE_TYPE_WIFI +# define NM_DEVICE_TYPE_WIFI 2 +#endif + +// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NM80211ApFlags +// Wifi network has no security +#ifndef NM_802_11_AP_SEC_NONE +# define NM_802_11_AP_SEC_NONE 0x00000000 +#endif + +// Wifi network has WEP (40 bits) +#ifndef NM_802_11_AP_SEC_PAIR_WEP40 +# define NM_802_11_AP_SEC_PAIR_WEP40 0x00000001 +#endif + +// Wifi network has WEP (104 bits) +#ifndef NM_802_11_AP_SEC_PAIR_WEP104 +# define NM_802_11_AP_SEC_PAIR_WEP104 0x00000002 +#endif + +#define NM_802_11_AP_SEC_WEAK_CRYPTO \ + (NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104) + +constexpr const char* DBUS_NETWORKMANAGER = "org.freedesktop.NetworkManager"; + +namespace { +Logger logger("LinuxNetworkWatcherWorker"); +} + +static inline bool checkUnsecureFlags(int rsnFlags, int wpaFlags) { + // If neither WPA nor WPA2/RSN are supported, then the network is unencrypted + if (rsnFlags == NM_802_11_AP_SEC_NONE && wpaFlags == NM_802_11_AP_SEC_NONE) { + return false; + } + + // Consider the user of weak cryptography to be unsecure + if ((rsnFlags & NM_802_11_AP_SEC_WEAK_CRYPTO) || + (wpaFlags & NM_802_11_AP_SEC_WEAK_CRYPTO)) { + return false; + } + // Otherwise, the network is secured with reasonable cryptography + return true; +} + +LinuxNetworkWatcherWorker::LinuxNetworkWatcherWorker(QThread* thread) { + MZ_COUNT_CTOR(LinuxNetworkWatcherWorker); + moveToThread(thread); +} + +LinuxNetworkWatcherWorker::~LinuxNetworkWatcherWorker() { + MZ_COUNT_DTOR(LinuxNetworkWatcherWorker); +} + +void LinuxNetworkWatcherWorker::initialize() { + logger.debug() << "initialize"; + + logger.debug() + << "Retrieving the list of wifi network devices from NetworkManager"; + + // To know the NeworkManager DBus methods and properties, read the official + // documentation: + // https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html + + QDBusInterface nm(DBUS_NETWORKMANAGER, "/org/freedesktop/NetworkManager", + DBUS_NETWORKMANAGER, QDBusConnection::systemBus()); + if (!nm.isValid()) { + logger.error() + << "Failed to connect to the network manager via system dbus"; + return; + } + + QDBusMessage msg = nm.call("GetDevices"); + QDBusArgument arg = msg.arguments().at(0).value(); + if (arg.currentType() != QDBusArgument::ArrayType) { + logger.error() << "Expected an array of devices"; + return; + } + + QList paths = qdbus_cast >(arg); + for (const QDBusObjectPath& path : paths) { + QString devicePath = path.path(); + QDBusInterface device(DBUS_NETWORKMANAGER, devicePath, + "org.freedesktop.NetworkManager.Device", + QDBusConnection::systemBus()); + if (device.property("DeviceType").toInt() != NM_DEVICE_TYPE_WIFI) { + continue; + } + + logger.debug() << "Found a wifi device:" << devicePath; + m_devicePaths.append(devicePath); + + // Here we monitor the changes. + QDBusConnection::systemBus().connect( + DBUS_NETWORKMANAGER, devicePath, "org.freedesktop.DBus.Properties", + "PropertiesChanged", this, + SLOT(propertyChanged(QString, QVariantMap, QStringList))); + } + + if (m_devicePaths.isEmpty()) { + logger.warning() << "No wifi devices found"; + return; + } + + // We could be already be activated. + checkDevices(); +} + +void LinuxNetworkWatcherWorker::propertyChanged(QString interface, + QVariantMap properties, + QStringList list) { + Q_UNUSED(list); + + logger.debug() << "Properties changed for interface" << interface; + + if (!properties.contains("ActiveAccessPoint")) { + logger.debug() << "Access point did not changed. Ignoring the changes"; + return; + } + + checkDevices(); +} + +void LinuxNetworkWatcherWorker::checkDevices() { + logger.debug() << "Checking devices"; + + for (const QString& devicePath : m_devicePaths) { + QDBusInterface wifiDevice(DBUS_NETWORKMANAGER, devicePath, + "org.freedesktop.NetworkManager.Device.Wireless", + QDBusConnection::systemBus()); + + // Check the access point path + QString accessPointPath = wifiDevice.property("ActiveAccessPoint") + .value() + .path(); + if (accessPointPath.isEmpty()) { + logger.warning() << "No access point found"; + continue; + } + + QDBusInterface ap(DBUS_NETWORKMANAGER, accessPointPath, + "org.freedesktop.NetworkManager.AccessPoint", + QDBusConnection::systemBus()); + + QVariant rsnFlags = ap.property("RsnFlags"); + QVariant wpaFlags = ap.property("WpaFlags"); + if (!rsnFlags.isValid() || !wpaFlags.isValid()) { + // We are probably not connected. + continue; + } + + if (!checkUnsecureFlags(rsnFlags.toInt(), wpaFlags.toInt())) { + QString ssid = ap.property("Ssid").toString(); + QString bssid = ap.property("HwAddress").toString(); + + // We have found 1 unsecured network. We don't need to check other wifi + // network devices. + logger.warning() << "Unsecured AP detected!" + << "rsnFlags:" << rsnFlags.toInt() + << "wpaFlags:" << wpaFlags.toInt() + << "ssid:" << logger.sensitive(ssid); + emit unsecuredNetwork(ssid, bssid); + break; + } + } +} diff --git a/client/platforms/linux/linuxnetworkwatcherworker.h b/client/platforms/linux/linuxnetworkwatcherworker.h new file mode 100644 index 00000000..cc4c6a36 --- /dev/null +++ b/client/platforms/linux/linuxnetworkwatcherworker.h @@ -0,0 +1,41 @@ +/* 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/. */ + +#ifndef LINUXNETWORKWATCHERWORKER_H +#define LINUXNETWORKWATCHERWORKER_H + +#include +#include +#include + +class QThread; + +class LinuxNetworkWatcherWorker final : public QObject { + Q_OBJECT + Q_DISABLE_COPY_MOVE(LinuxNetworkWatcherWorker) + + public: + explicit LinuxNetworkWatcherWorker(QThread* thread); + ~LinuxNetworkWatcherWorker(); + + void checkDevices(); + + signals: + void unsecuredNetwork(const QString& networkName, const QString& networkId); + + public slots: + void initialize(); + + private slots: + void propertyChanged(QString interface, QVariantMap properties, + QStringList list); + + private: + // We collect the list of DBus wifi network device paths during the + // initialization. When a property of them changes, we check if the access + // point is active and unsecure. + QStringList m_devicePaths; +}; + +#endif // LINUXNETWORKWATCHERWORKER_H diff --git a/client/protocols/wireguardprotocol.cpp b/client/protocols/wireguardprotocol.cpp index 3091655d..91903b2b 100644 --- a/client/protocols/wireguardprotocol.cpp +++ b/client/protocols/wireguardprotocol.cpp @@ -16,9 +16,7 @@ WireguardProtocol::WireguardProtocol(const QJsonObject &configuration, QObject* writeWireguardConfiguration(configuration); // MZ -#if defined(MZ_LINUX) - //m_impl.reset(new LinuxController()); -#elif defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) m_impl.reset(new LocalSocketController()); connect(m_impl.get(), &ControllerImpl::connected, this, [this](const QString& pubkey, const QDateTime& connectionTimestamp) { emit connectionStateChanged(VpnProtocol::Connected); @@ -38,7 +36,7 @@ WireguardProtocol::~WireguardProtocol() void WireguardProtocol::stop() { -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) stopMzImpl(); return; #endif @@ -98,7 +96,7 @@ void WireguardProtocol::stop() setConnectionState(VpnProtocol::Disconnected); } -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) ErrorCode WireguardProtocol::startMzImpl() { @@ -126,7 +124,7 @@ void WireguardProtocol::writeWireguardConfiguration(const QJsonObject &configura m_configFile.write(jConfig.value(config_key::config).toString().toUtf8()); m_configFile.close(); -#ifdef Q_OS_LINUX +#if 0 if (IpcClient::Interface()) { QRemoteObjectPendingReply result = IpcClient::Interface()->copyWireguardConfig(m_configFile.fileName()); if (result.returnValue()) { @@ -171,7 +169,7 @@ ErrorCode WireguardProtocol::start() return lastError(); } -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) return startMzImpl(); #endif diff --git a/client/protocols/wireguardprotocol.h b/client/protocols/wireguardprotocol.h index 6f530758..dea8d6d9 100644 --- a/client/protocols/wireguardprotocol.h +++ b/client/protocols/wireguardprotocol.h @@ -23,7 +23,7 @@ public: ErrorCode start() override; void stop() override; -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) ErrorCode startMzImpl(); ErrorCode stopMzImpl(); #endif @@ -47,7 +47,7 @@ private: bool m_isConfigLoaded = false; -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) +#if defined(Q_OS_MAC) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) QScopedPointer m_impl; #endif }; diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 72c5b09b..20ed8cb6 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -6,9 +6,10 @@ project(${PROJECT}) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 REQUIRED COMPONENTS Core Network Widgets RemoteObjects Core5Compat) +find_package(Qt6 REQUIRED COMPONENTS DBus Core Network Widgets RemoteObjects Core5Compat) qt_standard_project_setup() + configure_file(${CMAKE_SOURCE_DIR}/version.h.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) set(HEADERS @@ -91,7 +92,7 @@ if(UNIX) ) endif() -if (WIN32 OR APPLE) +if (WIN32 OR APPLE OR LINUX) set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/../../client/daemon/daemon.h ${CMAKE_CURRENT_LIST_DIR}/../../client/daemon/daemonlocalserver.h @@ -198,12 +199,32 @@ if(APPLE) endif() if(LINUX) + set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/router_linux.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.h ) set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/router_linux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcher.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.cpp ) endif() @@ -217,7 +238,7 @@ include_directories( ) add_executable(${PROJECT} ${SOURCES} ${HEADERS}) -target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat ${LIBS}) +target_link_libraries(${PROJECT} PRIVATE Qt6::Core Qt6::Widgets Qt6::Network Qt6::RemoteObjects Qt6::Core5Compat Qt6::DBus ${LIBS}) target_compile_definitions(${PROJECT} PRIVATE "MZ_$") if(CMAKE_BUILD_TYPE STREQUAL "Debug") diff --git a/service/server/localserver.cpp b/service/server/localserver.cpp index 709ad693..3e1b0954 100644 --- a/service/server/localserver.cpp +++ b/service/server/localserver.cpp @@ -40,12 +40,16 @@ LocalServer::LocalServer(QObject *parent) : QObject(parent), } }); -#if defined(Q_OS_MAC) || defined(Q_OS_WIN) // Init Mozilla Wireguard Daemon if (!server.initialize()) { logger.error() << "Failed to initialize the server"; return; } + +#ifdef Q_OS_LINUX + // Signal handling for a proper shutdown. + QObject::connect(qApp, &QCoreApplication::aboutToQuit, + []() { LinuxDaemon::instance()->deactivate(); }); #endif #ifdef Q_OS_MAC diff --git a/service/server/localserver.h b/service/server/localserver.h index b5264120..4a6648a5 100644 --- a/service/server/localserver.h +++ b/service/server/localserver.h @@ -10,13 +10,18 @@ #include "ipcserver.h" -#ifdef Q_OS_WIN #include "../../client/daemon/daemonlocalserver.h" + + +#ifdef Q_OS_WIN #include "windows/daemon/windowsdaemon.h" #endif +#ifdef Q_OS_LINUX +#include "linux/daemon/linuxdaemon.h" +#endif + #ifdef Q_OS_MAC -#include "../../client/daemon/daemonlocalserver.h" #include "macos/daemon/macosdaemon.h" #endif @@ -31,13 +36,14 @@ class LocalServer : public QObject public: explicit LocalServer(QObject* parent = nullptr); ~LocalServer(); - QSharedPointer m_server; - IpcServer m_ipcServer; QRemoteObjectHost m_serverNode; bool m_isRemotingEnabled = false; - +#ifdef Q_OS_LINUX + DaemonLocalServer server{qApp}; + LinuxDaemon daemon; +#endif #ifdef Q_OS_WIN DaemonLocalServer server{qApp}; WindowsDaemon daemon; diff --git a/service/server/router_linux.cpp b/service/server/router_linux.cpp index 3517f8e6..d717ce9c 100644 --- a/service/server/router_linux.cpp +++ b/service/server/router_linux.cpp @@ -14,6 +14,17 @@ #include #include #include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include RouterLinux &RouterLinux::Instance() @@ -22,6 +33,123 @@ RouterLinux &RouterLinux::Instance() return s; } +#define BUFFER_SIZE 4096 + +QString RouterLinux::getgatewayandiface() +{ + int received_bytes = 0, msg_len = 0, route_attribute_len = 0; + int sock = -1, msgseq = 0; + struct nlmsghdr *nlh, *nlmsg; + struct rtmsg *route_entry; + // This struct contain route attributes (route type) + struct rtattr *route_attribute; + char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE]; + char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; + char *ptr = buffer; + struct timeval tv; + + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { + perror("socket failed"); + return ""; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(gateway_address, 0, sizeof(gateway_address)); + memset(interface, 0, sizeof(interface)); + memset(buffer, 0, sizeof(buffer)); + + /* point the header and the msg structure pointers into the buffer */ + nlmsg = (struct nlmsghdr *)msgbuf; + + /* Fill in the nlmsg header*/ + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . + nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. + nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. + nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. + + /* 1 Sec Timeout to avoid stall */ + tv.tv_sec = 1; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); + /* send msg */ + if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { + perror("send failed"); + return ""; + } + + /* receive response */ + do + { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if (received_bytes < 0) { + perror("Error in recv"); + return ""; + } + + nlh = (struct nlmsghdr *) ptr; + + /* Check if the header is valid */ + if((NLMSG_OK(nlmsg, received_bytes) == 0) || + (nlmsg->nlmsg_type == NLMSG_ERROR)) + { + perror("Error in received packet"); + return ""; + } + + /* If we received all data break */ + if (nlh->nlmsg_type == NLMSG_DONE) + break; + else { + ptr += received_bytes; + msg_len += received_bytes; + } + + /* Break if its not a multi part message */ + if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } + while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); + + /* parse response */ + for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) + { + /* Get the route data */ + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + /* We are just interested in main routing table */ + if (route_entry->rtm_table != RT_TABLE_MAIN) + continue; + + route_attribute = (struct rtattr *) RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(nlh); + + /* Loop through all attributes */ + for ( ; RTA_OK(route_attribute, route_attribute_len); + route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) + { + switch(route_attribute->rta_type) { + case RTA_OIF: + if_indextoname(*(int *)RTA_DATA(route_attribute), interface); + break; + case RTA_GATEWAY: + inet_ntop(AF_INET, RTA_DATA(route_attribute), + gateway_address, sizeof(gateway_address)); + break; + default: + break; + } + } + + if ((*gateway_address) && (*interface)) { + qDebug().noquote() << "Gateway " << gateway_address << " for interface " << interface; + break; + } + } + close(sock); + return gateway_address; +} + + bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const int &sock) { QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet); @@ -29,7 +157,7 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to add invalid route: " << ip << gw; - return false; + return true; } struct rtentry route; @@ -53,11 +181,11 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (int err = ioctl(sock, SIOCADDRT, &route) < 0) { - qDebug().noquote() << "route add error: gw " - << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr - << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr - << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; - return false; + // qDebug().noquote() << "route add error: gw " + // << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr + // << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr + // << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; + // return false; } m_addedRoutes.append({ipWithSubnet, gw}); @@ -99,7 +227,7 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to remove invalid route: " << ip << gw; - return false; + return true; } if (ip == "0.0.0.0") { @@ -129,8 +257,8 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (ioctl(sock, SIOCDELRT, &route) < 0) { - qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; - return false; + // qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; + // return false; } return true; } diff --git a/service/server/router_linux.h b/service/server/router_linux.h index 6da20b7d..5b4897bd 100644 --- a/service/server/router_linux.h +++ b/service/server/router_linux.h @@ -27,6 +27,7 @@ public: bool clearSavedRoutes(); bool routeDelete(const QString &ip, const QString &gw, const int &sock); bool routeDeleteList(const QString &gw, const QStringList &ips); + QString getgatewayandiface(); void flushDns(); public slots: From ff41b26e94892687bc71be911b4725c24397cc50 Mon Sep 17 00:00:00 2001 From: "vladimir.kuznetsov" Date: Tue, 19 Sep 2023 18:45:06 +0500 Subject: [PATCH 2/6] added parsing of wireguard config parameters when importing native configs --- client/ui/pages_logic/StartPageLogic.cpp | 166 ++++++++++++++--------- 1 file changed, 104 insertions(+), 62 deletions(-) diff --git a/client/ui/pages_logic/StartPageLogic.cpp b/client/ui/pages_logic/StartPageLogic.cpp index c1670b70..79bd24c0 100644 --- a/client/ui/pages_logic/StartPageLogic.cpp +++ b/client/ui/pages_logic/StartPageLogic.cpp @@ -1,72 +1,73 @@ #include "StartPageLogic.h" #include "ViewConfigLogic.h" -#include "core/errorstrings.h" +#include "../uilogic.h" #include "configurators/ssh_configurator.h" #include "configurators/vpn_configurator.h" -#include "../uilogic.h" -#include "utilities.h" +#include "core/errorstrings.h" #include "core/servercontroller.h" +#include "utilities.h" +#include #include #include -#include #ifdef Q_OS_ANDROID -#include -#include "../../platforms/android/androidutils.h" -#include "../../platforms/android/android_controller.h" + #include "../../platforms/android/android_controller.h" + #include "../../platforms/android/androidutils.h" + #include #endif #ifdef Q_OS_IOS -#include + #include #endif -namespace { -enum class ConfigTypes { - Amnezia, - OpenVpn, - WireGuard -}; - -ConfigTypes checkConfigFormat(const QString &config) +namespace { - const QString openVpnConfigPatternCli = "client"; - const QString openVpnConfigPatternProto1 = "proto tcp"; - const QString openVpnConfigPatternProto2 = "proto udp"; - const QString openVpnConfigPatternDriver1 = "dev tun"; - const QString openVpnConfigPatternDriver2 = "dev tap"; + enum class ConfigTypes { + Amnezia, + OpenVpn, + WireGuard + }; - const QString wireguardConfigPatternSectionInterface = "[Interface]"; - const QString wireguardConfigPatternSectionPeer = "[Peer]"; + ConfigTypes checkConfigFormat(const QString &config) + { + const QString openVpnConfigPatternCli = "client"; + const QString openVpnConfigPatternProto1 = "proto tcp"; + const QString openVpnConfigPatternProto2 = "proto udp"; + const QString openVpnConfigPatternDriver1 = "dev tun"; + const QString openVpnConfigPatternDriver2 = "dev tap"; - if (config.contains(openVpnConfigPatternCli) && - (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) && - (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { - return ConfigTypes::OpenVpn; - } else if (config.contains(wireguardConfigPatternSectionInterface) && - config.contains(wireguardConfigPatternSectionPeer)) - return ConfigTypes::WireGuard; - return ConfigTypes::Amnezia; -} + const QString wireguardConfigPatternSectionInterface = "[Interface]"; + const QString wireguardConfigPatternSectionPeer = "[Peer]"; + + if (config.contains(openVpnConfigPatternCli) + && (config.contains(openVpnConfigPatternProto1) || config.contains(openVpnConfigPatternProto2)) + && (config.contains(openVpnConfigPatternDriver1) || config.contains(openVpnConfigPatternDriver2))) { + return ConfigTypes::OpenVpn; + } else if (config.contains(wireguardConfigPatternSectionInterface) + && config.contains(wireguardConfigPatternSectionPeer)) + return ConfigTypes::WireGuard; + return ConfigTypes::Amnezia; + } } -StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent): - PageLogicBase(logic, parent), - m_pushButtonConnectEnabled{true}, - m_pushButtonConnectText{tr("Connect")}, - m_pushButtonConnectKeyChecked{false}, - m_labelWaitInfoVisible{true}, - m_pushButtonBackFromStartVisible{true}, - m_ipAddressPortRegex{Utils::ipAddressPortRegExp()} +StartPageLogic::StartPageLogic(UiLogic *logic, QObject *parent) + : PageLogicBase(logic, parent), + m_pushButtonConnectEnabled { true }, + m_pushButtonConnectText { tr("Connect") }, + m_pushButtonConnectKeyChecked { false }, + m_labelWaitInfoVisible { true }, + m_pushButtonBackFromStartVisible { true }, + m_ipAddressPortRegex { Utils::ipAddressPortRegExp() } { #ifdef Q_OS_ANDROID // Set security screen for Android app AndroidUtils::runOnAndroidThreadSync([]() { QJniObject activity = AndroidUtils::getActivity(); QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); - if (window.isValid()){ + if (window.isValid()) { const int FLAG_SECURE = 8192; window.callMethod("addFlags", "(I)V", FLAG_SECURE); } @@ -93,17 +94,13 @@ void StartPageLogic::onUpdatePage() void StartPageLogic::onPushButtonConnect() { - if (pushButtonConnectKeyChecked()){ - if (lineEditIpText().isEmpty() || - lineEditLoginText().isEmpty() || - textEditSshKeyText().isEmpty() ) { + if (pushButtonConnectKeyChecked()) { + if (lineEditIpText().isEmpty() || lineEditLoginText().isEmpty() || textEditSshKeyText().isEmpty()) { set_labelWaitInfoText(tr("Please fill in all fields")); return; } } else { - if (lineEditIpText().isEmpty() || - lineEditLoginText().isEmpty() || - lineEditPasswordText().isEmpty() ) { + if (lineEditIpText().isEmpty() || lineEditLoginText().isEmpty() || lineEditPasswordText().isEmpty()) { set_labelWaitInfoText(tr("Please fill in all fields")); return; } @@ -178,7 +175,8 @@ void StartPageLogic::onPushButtonConnect() set_pushButtonConnectText(tr("Connect")); uiLogic()->m_installCredentials = serverCredentials; - if (ok) emit uiLogic()->goToPage(Page::NewServer); + if (ok) + emit uiLogic()->goToPage(Page::NewServer); } void StartPageLogic::onPushButtonImport() @@ -189,22 +187,24 @@ void StartPageLogic::onPushButtonImport() void StartPageLogic::onPushButtonImportOpenFile() { QString fileName = UiLogic::getOpenFileName(Q_NULLPTR, tr("Open config file"), - QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), "*.vpn *.ovpn *.conf"); - if (fileName.isEmpty()) return; + QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), + "*.vpn *.ovpn *.conf"); + if (fileName.isEmpty()) + return; QFile file(fileName); - + #ifdef Q_OS_IOS CFURLRef url = CFURLCreateWithFileSystemPath( - kCFAllocatorDefault, CFStringCreateWithCharacters(0, reinterpret_cast(fileName.unicode()), - fileName.length()), - kCFURLPOSIXPathStyle, 0); - + kCFAllocatorDefault, + CFStringCreateWithCharacters(0, reinterpret_cast(fileName.unicode()), fileName.length()), + kCFURLPOSIXPathStyle, 0); + if (!CFURLStartAccessingSecurityScopedResource(url)) { qDebug() << "Could not access path " << QUrl::fromLocalFile(fileName).toString(); } #endif - + file.open(QIODevice::ReadOnly); QByteArray data = file.readAll(); @@ -242,8 +242,7 @@ bool StartPageLogic::importConnection(const QJsonObject &profile) // check config uiLogic()->pageLogic()->set_configJson(profile); emit uiLogic()->goToPage(Page::ViewConfig); - } - else { + } else { qDebug() << "Failed to import profile"; qDebug().noquote() << QJsonDocument(profile).toJson(); return false; @@ -314,7 +313,6 @@ bool StartPageLogic::importConnectionFromOpenVpnConfig(const QString &config) o[config_key::defaultContainer] = "amnezia-openvpn"; o[config_key::description] = m_settings->nextAvailableServerName(); - const static QRegularExpression dnsRegExp("dhcp-option DNS (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatchIterator dnsMatch = dnsRegExp.globalMatch(config); if (dnsMatch.hasNext()) { @@ -338,9 +336,51 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) QRegularExpressionMatch hostNameAndPortMatch = hostNameAndPortRegExp.match(config); QString hostName; QString port; - if (hostNameAndPortMatch.hasMatch()) { + if (hostNameAndPortMatch.hasCaptured(1)) { hostName = hostNameAndPortMatch.captured(1); + } else { + return importConnection(QJsonObject()); + } + + if (hostNameAndPortMatch.hasCaptured(2)) { port = hostNameAndPortMatch.captured(2); + } else { + port = protocols::wireguard::defaultPort; + } + + lastConfig[config_key::hostName] = hostName; + lastConfig[config_key::port] = port.toInt(); + + const static QRegularExpression clientPrivKeyRegExp("PrivateKey = (.*)"); + QRegularExpressionMatch clientPrivKeyMatch = clientPrivKeyRegExp.match(config); + if (clientPrivKeyMatch.hasMatch()) { + lastConfig[config_key::client_priv_key] = clientPrivKeyMatch.captured(1); + } else { + return importConnection(QJsonObject()); + } + + const static QRegularExpression clientIpRegExp("Address = (\\d+\\.\\d+\\.\\d+\\.\\d+)"); + QRegularExpressionMatch clientIpMatch = clientIpRegExp.match(config); + if (clientIpMatch.hasMatch()) { + lastConfig[config_key::client_ip] = clientIpMatch.captured(1); + } else { + return importConnection(QJsonObject()); + } + + const static QRegularExpression pskKeyRegExp("PresharedKey = (.*)"); + QRegularExpressionMatch pskKeyMatch = pskKeyRegExp.match(config); + if (pskKeyMatch.hasMatch()) { + lastConfig[config_key::psk_key] = pskKeyMatch.captured(1); + } else { + return importConnection(QJsonObject()); + } + + const static QRegularExpression serverPubKeyRegExp("PublicKey = (.*)"); + QRegularExpressionMatch serverPubKeyMatch = serverPubKeyRegExp.match(config); + if (serverPubKeyMatch.hasMatch()) { + lastConfig[config_key::server_pub_key] = serverPubKeyMatch.captured(1); + } else { + return importConnection(QJsonObject()); } QJsonObject wireguardConfig; @@ -361,7 +401,9 @@ bool StartPageLogic::importConnectionFromWireguardConfig(const QString &config) o[config_key::defaultContainer] = "amnezia-wireguard"; o[config_key::description] = m_settings->nextAvailableServerName(); - const static QRegularExpression dnsRegExp("DNS = (\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); + const static QRegularExpression dnsRegExp( + "DNS = " + "(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b).*(\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b)"); QRegularExpressionMatch dnsMatch = dnsRegExp.match(config); if (dnsMatch.hasMatch()) { o[config_key::dns1] = dnsMatch.captured(1); From f5ab034aeb6b88ca6362b22ccb3986fd2d13a729 Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Tue, 19 Sep 2023 17:59:04 -0400 Subject: [PATCH 3/6] WG routing rework for Linux --- .../linux/daemon/linuxroutemonitor.cpp | 371 ++++++++++++++---- .../linux/daemon/linuxroutemonitor.h | 25 +- client/platforms/linux/daemon/pidtracker.cpp | 228 ----------- client/platforms/linux/daemon/pidtracker.h | 72 ---- service/server/CMakeLists.txt | 2 - service/server/router_linux.cpp | 146 +------ 6 files changed, 314 insertions(+), 530 deletions(-) delete mode 100644 client/platforms/linux/daemon/pidtracker.cpp delete mode 100644 client/platforms/linux/daemon/pidtracker.h diff --git a/client/platforms/linux/daemon/linuxroutemonitor.cpp b/client/platforms/linux/daemon/linuxroutemonitor.cpp index 80f510b7..f0c49eb6 100644 --- a/client/platforms/linux/daemon/linuxroutemonitor.cpp +++ b/client/platforms/linux/daemon/linuxroutemonitor.cpp @@ -4,27 +4,21 @@ #include "linuxroutemonitor.h" -#include "router_linux.h" - -#include -#include -#include -#include -#include -#include #include - -#include -#include - -#include -#include - #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include "leakdetector.h" #include "logger.h" @@ -32,31 +26,57 @@ namespace { Logger logger("LinuxRouteMonitor"); } // namespace + +typedef struct wg_allowedip { + uint16_t family; + union { + struct in_addr ip4; + struct in6_addr ip6; + }; + uint8_t cidr; + struct wg_allowedip *next_allowedip; +} wg_allowedip; + +constexpr const char* WG_INTERFACE = "amn0"; + +static void nlmsg_append_attr(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, const void* attrdata, + size_t attrlen); +static void nlmsg_append_attr32(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, uint32_t value); + +static bool buildAllowedIp(wg_allowedip* ip, const IPAddress& prefix); + + LinuxRouteMonitor::LinuxRouteMonitor(const QString& ifname, QObject* parent) : QObject(parent), m_ifname(ifname) { MZ_COUNT_CTOR(LinuxRouteMonitor); logger.debug() << "LinuxRouteMonitor created."; - m_rtsock = socket(PF_ROUTE, SOCK_RAW, 0); - if (m_rtsock < 0) { - logger.error() << "Failed to create routing socket:" << strerror(errno); - return; + m_nlsock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); + if (m_nlsock < 0) { + logger.warning() << "Failed to create netlink socket:" << strerror(errno); } - RouterLinux &router = RouterLinux::Instance(); - m_defaultGatewayIpv4 = router.getgatewayandiface().toUtf8(); + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + nladdr.nl_pid = getpid(); + if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) != 0) { + logger.warning() << "Failed to bind netlink socket:" << strerror(errno); + } - m_ifindex = if_nametoindex(qPrintable(ifname)); - m_notifier = new QSocketNotifier(m_rtsock, QSocketNotifier::Read, this); + m_notifier = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); + connect(m_notifier, &QSocketNotifier::activated, this, + &LinuxRouteMonitor::nlsockReady); } LinuxRouteMonitor::~LinuxRouteMonitor() { MZ_COUNT_DTOR(LinuxRouteMonitor); - flushExclusionRoutes(); - if (m_rtsock >= 0) { - close(m_rtsock); + if (m_nlsock >= 0) { + close(m_nlsock); } - logger.debug() << "LinuxRouteMonitor destroyed."; + logger.debug() << "WireguardUtilsLinux destroyed."; } // Compare memory against zero. @@ -69,65 +89,266 @@ static int memcmpzero(const void* data, size_t len) { } bool LinuxRouteMonitor::insertRoute(const IPAddress& prefix) { - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + logger.debug() << "Adding route to" << prefix.toString(); - struct ifreq ifc; - int res; - - if(temp_sock < 0) - return -1; - strcpy(ifc.ifr_name, m_ifname.toUtf8()); - - res = ioctl(temp_sock, SIOCGIFADDR, &ifc); - if(res < 0) - return -1; - - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); - return router.routeAdd(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); + const int flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + return rtmSendRoute(RTM_NEWROUTE, flags, RTN_UNICAST, prefix); } bool LinuxRouteMonitor::deleteRoute(const IPAddress& prefix) { - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + logger.debug() << "Removing route to" << prefix.toString(); - struct ifreq ifc; - int res; - if(temp_sock < 0) - temp_sock -1; - strcpy(ifc.ifr_name, m_ifname.toUtf8()); - - res = ioctl(temp_sock, SIOCGIFADDR, &ifc); - if(res < 0) - return -1; - - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_ifname " << inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr); - return router.routeDelete(prefix.toString(), inet_ntoa(((struct sockaddr_in*)&ifc.ifr_addr)->sin_addr), temp_sock); + const int flags = NLM_F_REQUEST | NLM_F_ACK; + return rtmSendRoute(RTM_DELROUTE, flags, RTN_UNICAST, prefix); } bool LinuxRouteMonitor::addExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Adding exclusion route for" - << logger.sensitive(prefix.toString()); - - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; - return router.routeAdd(prefix.toString(), m_defaultGatewayIpv4, temp_sock); - // Otherwise, the default route isn't known yet. Do nothing. - return true; + logger.debug() << "Adding exclusion route for" + << prefix.toString(); + const int flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; + return rtmSendRoute(RTM_NEWROUTE, flags, RTN_THROW, prefix); } bool LinuxRouteMonitor::deleteExclusionRoute(const IPAddress& prefix) { - logger.debug() << "Deleting exclusion route for" - << logger.sensitive(prefix.toString()); - - int temp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - RouterLinux &router = RouterLinux::Instance(); - logger.debug() << "prefix.toString() " << prefix.toString() << " m_defaultGatewayIpv4 " << m_defaultGatewayIpv4; - return router.routeDelete(prefix.toString(), m_defaultGatewayIpv4, temp_sock); + logger.debug() << "Removing exclusion route for" + << prefix.toString(); + const int flags = NLM_F_REQUEST | NLM_F_ACK; + return rtmSendRoute(RTM_DELROUTE, flags, RTN_THROW, prefix); } -void LinuxRouteMonitor::flushExclusionRoutes() { - RouterLinux &router = RouterLinux::Instance(); - router.clearSavedRoutes(); +bool LinuxRouteMonitor::rtmSendRoute(int action, int flags, int type, + const IPAddress& prefix) { + constexpr size_t rtm_max_size = sizeof(struct rtmsg) + + 2 * RTA_SPACE(sizeof(uint32_t)) + + RTA_SPACE(sizeof(struct in6_addr)); + wg_allowedip ip; + if (!buildAllowedIp(&ip, prefix)) { + logger.warning() << "Invalid destination prefix"; + return false; + } + + char buf[NLMSG_SPACE(rtm_max_size)]; + struct nlmsghdr* nlmsg = reinterpret_cast(buf); + struct rtmsg* rtm = static_cast(NLMSG_DATA(nlmsg)); + + memset(buf, 0, sizeof(buf)); + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = action; + nlmsg->nlmsg_flags = flags; + nlmsg->nlmsg_pid = getpid(); + nlmsg->nlmsg_seq = m_nlseq++; + rtm->rtm_dst_len = ip.cidr; + rtm->rtm_family = ip.family; + rtm->rtm_type = type; + rtm->rtm_table = RT_TABLE_UNSPEC; + rtm->rtm_protocol = RTPROT_BOOT; + rtm->rtm_scope = RT_SCOPE_UNIVERSE; + + if (rtm->rtm_family == AF_INET6) { + nlmsg_append_attr(nlmsg, sizeof(buf), RTA_DST, &ip.ip6, sizeof(ip.ip6)); + } else { + nlmsg_append_attr(nlmsg, sizeof(buf), RTA_DST, &ip.ip4, sizeof(ip.ip4)); + } + + if (rtm->rtm_type == RTN_UNICAST) { + int index = if_nametoindex(WG_INTERFACE); + + if (index <= 0) { + logger.error() << "if_nametoindex() failed:" << strerror(errno); + return false; + } + nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index); + } + + if (rtm->rtm_type == RTN_THROW) { + int index = if_nametoindex(getgatewayandiface().toUtf8()); + if (index <= 0) { + logger.error() << "if_nametoindex() failed:" << strerror(errno); + return false; + } + nlmsg_append_attr32(nlmsg, sizeof(buf), RTA_OIF, index); + } + + struct sockaddr_nl nladdr; + memset(&nladdr, 0, sizeof(nladdr)); + nladdr.nl_family = AF_NETLINK; + size_t result = sendto(m_nlsock, buf, nlmsg->nlmsg_len, 0, + (struct sockaddr*)&nladdr, sizeof(nladdr)); + + return (result == nlmsg->nlmsg_len); +} + +static void nlmsg_append_attr(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, const void* attrdata, + size_t attrlen) { + size_t newlen = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_SPACE(attrlen); + if (newlen <= maxlen) { + char* buf = reinterpret_cast(nlmsg) + NLMSG_ALIGN(nlmsg->nlmsg_len); + struct rtattr* attr = reinterpret_cast(buf); + attr->rta_type = attrtype; + attr->rta_len = RTA_LENGTH(attrlen); + memcpy(RTA_DATA(attr), attrdata, attrlen); + nlmsg->nlmsg_len = newlen; + } +} + +static void nlmsg_append_attr32(struct nlmsghdr* nlmsg, size_t maxlen, + int attrtype, uint32_t value) { + nlmsg_append_attr(nlmsg, maxlen, attrtype, &value, sizeof(value)); +} + +void LinuxRouteMonitor::nlsockReady() { + char buf[1024]; + ssize_t len = recv(m_nlsock, buf, sizeof(buf), MSG_DONTWAIT); + if (len <= 0) { + return; + } + + struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; + while (NLMSG_OK(nlmsg, len)) { + if (nlmsg->nlmsg_type == NLMSG_DONE) { + return; + } + if (nlmsg->nlmsg_type != NLMSG_ERROR) { + nlmsg = NLMSG_NEXT(nlmsg, len); + continue; + } + struct nlmsgerr* err = static_cast(NLMSG_DATA(nlmsg)); + if (err->error != 0) { + logger.debug() << "Netlink request failed:" << strerror(-err->error); + } + nlmsg = NLMSG_NEXT(nlmsg, len); + } +} + +#define BUFFER_SIZE 4096 + +QString LinuxRouteMonitor::getgatewayandiface() +{ + int received_bytes = 0, msg_len = 0, route_attribute_len = 0; + int sock = -1, msgseq = 0; + struct nlmsghdr *nlh, *nlmsg; + struct rtmsg *route_entry; + // This struct contain route attributes (route type) + struct rtattr *route_attribute; + char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE]; + char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; + char *ptr = buffer; + struct timeval tv; + + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { + perror("socket failed"); + return ""; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(gateway_address, 0, sizeof(gateway_address)); + memset(interface, 0, sizeof(interface)); + memset(buffer, 0, sizeof(buffer)); + + /* point the header and the msg structure pointers into the buffer */ + nlmsg = (struct nlmsghdr *)msgbuf; + + /* Fill in the nlmsg header*/ + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . + nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. + nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. + nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. + + /* 1 Sec Timeout to avoid stall */ + tv.tv_sec = 1; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); + /* send msg */ + if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { + perror("send failed"); + return ""; + } + + /* receive response */ + do + { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if (received_bytes < 0) { + perror("Error in recv"); + return ""; + } + + nlh = (struct nlmsghdr *) ptr; + + /* Check if the header is valid */ + if((NLMSG_OK(nlmsg, received_bytes) == 0) || + (nlmsg->nlmsg_type == NLMSG_ERROR)) + { + perror("Error in received packet"); + return ""; + } + + /* If we received all data break */ + if (nlh->nlmsg_type == NLMSG_DONE) + break; + else { + ptr += received_bytes; + msg_len += received_bytes; + } + + /* Break if its not a multi part message */ + if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } + while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); + + /* parse response */ + for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) + { + /* Get the route data */ + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + /* We are just interested in main routing table */ + if (route_entry->rtm_table != RT_TABLE_MAIN) + continue; + + route_attribute = (struct rtattr *) RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(nlh); + + /* Loop through all attributes */ + for ( ; RTA_OK(route_attribute, route_attribute_len); + route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) + { + switch(route_attribute->rta_type) { + case RTA_OIF: + if_indextoname(*(int *)RTA_DATA(route_attribute), interface); + break; + case RTA_GATEWAY: + inet_ntop(AF_INET, RTA_DATA(route_attribute), + gateway_address, sizeof(gateway_address)); + break; + default: + break; + } + } + + if ((*gateway_address) && (*interface)) { + logger.debug() << "Gateway " << gateway_address << " for interface " << interface; + break; + } + } + close(sock); + return interface; +} + +static bool buildAllowedIp(wg_allowedip* ip, + const IPAddress& prefix) { + const char* addrString = qPrintable(prefix.address().toString()); + if (prefix.type() == QAbstractSocket::IPv4Protocol) { + ip->family = AF_INET; + ip->cidr = prefix.prefixLength(); + return inet_pton(AF_INET, addrString, &ip->ip4) == 1; + } + if (prefix.type() == QAbstractSocket::IPv6Protocol) { + ip->family = AF_INET6; + ip->cidr = prefix.prefixLength(); + return inet_pton(AF_INET6, addrString, &ip->ip6) == 1; + } + return false; } diff --git a/client/platforms/linux/daemon/linuxroutemonitor.h b/client/platforms/linux/daemon/linuxroutemonitor.h index 872fcb7e..f1c3ac1d 100644 --- a/client/platforms/linux/daemon/linuxroutemonitor.h +++ b/client/platforms/linux/daemon/linuxroutemonitor.h @@ -13,9 +13,6 @@ #include "ipaddress.h" -struct if_msghdr; -struct rt_msghdr; -struct sockaddr; class LinuxRouteMonitor final : public QObject { Q_OBJECT @@ -26,28 +23,24 @@ class LinuxRouteMonitor final : public QObject { bool insertRoute(const IPAddress& prefix); bool deleteRoute(const IPAddress& prefix); - int interfaceFlags() { return m_ifflags; } bool addExclusionRoute(const IPAddress& prefix); bool deleteExclusionRoute(const IPAddress& prefix); - void flushExclusionRoutes(); - private: static QString addrToString(const struct sockaddr* sa); static QString addrToString(const QByteArray& data); - - QList m_exclusionRoutes; - QByteArray m_defaultGatewayIpv4; - QByteArray m_defaultGatewayIpv6; - unsigned int m_defaultIfindexIpv4 = 0; - unsigned int m_defaultIfindexIpv6 = 0; - + bool rtmSendRoute(int action, int flags, int type, + const IPAddress& prefix); + QString getgatewayandiface(); QString m_ifname; unsigned int m_ifindex = 0; - int m_ifflags = 0; - int m_rtsock = -1; - int m_rtseq = 0; + int m_nlsock = -1; + int m_nlseq = 0; QSocketNotifier* m_notifier = nullptr; + + private slots: + void nlsockReady(); + }; #endif // LINUXROUTEMONITOR_H diff --git a/client/platforms/linux/daemon/pidtracker.cpp b/client/platforms/linux/daemon/pidtracker.cpp deleted file mode 100644 index 76d53219..00000000 --- a/client/platforms/linux/daemon/pidtracker.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/* 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 "pidtracker.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "leakdetector.h" -#include "logger.h" - -constexpr size_t CN_MCAST_MSG_SIZE = - sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op); - -namespace { -Logger logger("PidTracker"); -} - -PidTracker::PidTracker(QObject* parent) : QObject(parent) { - MZ_COUNT_CTOR(PidTracker); - logger.debug() << "PidTracker created."; - - m_nlsock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); - if (m_nlsock < 0) { - logger.error() << "Failed to create netlink socket:" << strerror(errno); - return; - } - - struct sockaddr_nl nladdr; - nladdr.nl_family = AF_NETLINK; - nladdr.nl_groups = CN_IDX_PROC; - nladdr.nl_pid = getpid(); - nladdr.nl_pad = 0; - if (bind(m_nlsock, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0) { - logger.error() << "Failed to bind netlink socket:" << strerror(errno); - close(m_nlsock); - m_nlsock = -1; - return; - } - - char buf[NLMSG_SPACE(CN_MCAST_MSG_SIZE)]; - struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; - struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(nlmsg); - enum proc_cn_mcast_op mcast_op = PROC_CN_MCAST_LISTEN; - - memset(buf, 0, sizeof(buf)); - nlmsg->nlmsg_len = NLMSG_LENGTH(CN_MCAST_MSG_SIZE); - nlmsg->nlmsg_type = NLMSG_DONE; - nlmsg->nlmsg_flags = 0; - nlmsg->nlmsg_seq = 0; - nlmsg->nlmsg_pid = getpid(); - - cnmsg->id.idx = CN_IDX_PROC; - cnmsg->id.val = CN_VAL_PROC; - cnmsg->seq = 0; - cnmsg->ack = 0; - cnmsg->len = sizeof(mcast_op); - memcpy(cnmsg->data, &mcast_op, sizeof(mcast_op)); - - if (send(m_nlsock, nlmsg, sizeof(buf), 0) != sizeof(buf)) { - logger.error() << "Failed to send netlink message:" << strerror(errno); - close(m_nlsock); - m_nlsock = -1; - return; - } - - m_socket = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); - connect(m_socket, &QSocketNotifier::activated, this, &PidTracker::readData); -} - -PidTracker::~PidTracker() { - MZ_COUNT_DTOR(PidTracker); - logger.debug() << "PidTracker destroyed."; - - m_processTree.clear(); - while (!m_processGroups.isEmpty()) { - ProcessGroup* group = m_processGroups.takeFirst(); - delete group; - } - - if (m_nlsock > 0) { - close(m_nlsock); - } -} - -ProcessGroup* PidTracker::track(const QString& name, int rootpid) { - ProcessGroup* group = m_processTree.value(rootpid, nullptr); - if (group) { - logger.warning() << "Ignoring attempt to track duplicate PID"; - return group; - } - group = new ProcessGroup(name, rootpid); - group->kthreads[rootpid] = 1; - group->refcount = 1; - - m_processGroups.append(group); - m_processTree[rootpid] = group; - - return group; -} - -void PidTracker::handleProcEvent(struct cn_msg* cnmsg) { - struct proc_event* ev = (struct proc_event*)cnmsg->data; - - if (ev->what == proc_event::PROC_EVENT_FORK) { - auto forkdata = &ev->event_data.fork; - /* If the child process already exists, track a new kernel thread. */ - ProcessGroup* group = m_processTree.value(forkdata->child_tgid, nullptr); - if (group) { - group->kthreads[forkdata->child_tgid]++; - return; - } - - /* Track a new userspace process if was forked from a known parent. */ - group = m_processTree.value(forkdata->parent_tgid, nullptr); - if (!group) { - return; - } - m_processTree[forkdata->child_tgid] = group; - group->kthreads[forkdata->child_tgid] = 1; - group->refcount++; - emit pidForked(group->name, forkdata->parent_tgid, forkdata->child_tgid); - } - - if (ev->what == proc_event::PROC_EVENT_EXIT) { - auto exitdata = &ev->event_data.exit; - ProcessGroup* group = m_processTree.value(exitdata->process_tgid, nullptr); - if (!group) { - return; - } - - /* Decrement the number of kernel threads in this userspace process. */ - uint threadcount = group->kthreads.value(exitdata->process_tgid, 0); - if (threadcount == 0) { - return; - } - if (threadcount > 1) { - group->kthreads[exitdata->process_tgid] = threadcount - 1; - return; - } - group->kthreads.remove(exitdata->process_tgid); - - /* A userspace process exits when all of its kernel threads exit. */ - Q_ASSERT(group->refcount > 0); - group->refcount--; - if (group->refcount == 0) { - emit terminated(group->name, group->rootpid); - m_processGroups.removeAll(group); - delete group; - } - } -} - -void PidTracker::readData() { - struct sockaddr_nl src; - socklen_t srclen = sizeof(src); - ssize_t recvlen; - - recvlen = recvfrom(m_nlsock, m_readBuf, sizeof(m_readBuf), MSG_DONTWAIT, - (struct sockaddr*)&src, &srclen); - if (recvlen == ENOBUFS) { - logger.error() - << "Failed to read netlink socket: buffer full, message dropped"; - return; - } - if (recvlen < 0) { - logger.error() << "Failed to read netlink socket:" << strerror(errno); - return; - } - if (srclen != sizeof(src)) { - logger.error() << "Failed to read netlink socket: invalid address length"; - return; - } - - /* We are only interested in process-control messages from the kernel */ - if ((src.nl_groups != CN_IDX_PROC) || (src.nl_pid != 0)) { - return; - } - - /* Handle the process-control messages. */ - struct nlmsghdr* msg; - for (msg = (struct nlmsghdr*)m_readBuf; NLMSG_OK(msg, recvlen); - msg = NLMSG_NEXT(msg, recvlen)) { - struct cn_msg* cnmsg = (struct cn_msg*)NLMSG_DATA(msg); - if (msg->nlmsg_type == NLMSG_NOOP) { - continue; - } - if ((msg->nlmsg_type == NLMSG_ERROR) || - (msg->nlmsg_type == NLMSG_OVERRUN)) { - break; - } - handleProcEvent(cnmsg); - if (msg->nlmsg_type == NLMSG_DONE) { - break; - } - } -} - -bool ProcessGroup::moveToCgroup(const QString& name) { - /* Do nothing if Cgroups are not supported. */ - if (name.isNull()) { - return true; - } - - QString cgProcsFile = name + "/cgroup.procs"; - FILE* fp = fopen(qPrintable(cgProcsFile), "w"); - if (!fp) { - return false; - } - - for (auto iterator = kthreads.constBegin(); iterator != kthreads.constEnd(); - ++iterator) { - fprintf(fp, "%d\n", iterator.key()); - fflush(fp); - } - fclose(fp); - return true; -} diff --git a/client/platforms/linux/daemon/pidtracker.h b/client/platforms/linux/daemon/pidtracker.h deleted file mode 100644 index dc632b8b..00000000 --- a/client/platforms/linux/daemon/pidtracker.h +++ /dev/null @@ -1,72 +0,0 @@ -/* 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/. */ - -#ifndef PIDTRACKER_H -#define PIDTRACKER_H - -#include -#include -#include -#include - -#include "leakdetector.h" - -struct cn_msg; - -class ProcessGroup { - public: - ProcessGroup(const QString& groupName, int groupRootPid, - const QString& groupState = "active") { - MZ_COUNT_CTOR(ProcessGroup); - name = groupName; - rootpid = groupRootPid; - state = groupState; - refcount = 0; - } - ~ProcessGroup() { MZ_COUNT_DTOR(ProcessGroup); } - - bool moveToCgroup(const QString& name); - - QHash kthreads; - QString name; - QString state; - int rootpid; - int refcount; -}; - -class PidTracker final : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(PidTracker) - - public: - explicit PidTracker(QObject* parent); - ~PidTracker(); - - ProcessGroup* track(const QString& name, int rootpid); - - QList pids() { return m_processTree.keys(); } - QList::iterator begin() { return m_processGroups.begin(); } - QList::iterator end() { return m_processGroups.end(); } - ProcessGroup* group(int pid) { return m_processTree.value(pid); } - - signals: - void pidForked(const QString& name, int parent, int child); - void pidExited(const QString& name, int pid); - void terminated(const QString& name, int rootpid); - - private: - void handleProcEvent(struct cn_msg*); - - private slots: - void readData(); - - private: - int m_nlsock; - char m_readBuf[2048]; - QSocketNotifier* m_socket = nullptr; - QHash m_processTree; - QList m_processGroups; -}; - -#endif // PIDTRACKER_H diff --git a/service/server/CMakeLists.txt b/service/server/CMakeLists.txt index 20ed8cb6..d2629a0b 100644 --- a/service/server/CMakeLists.txt +++ b/service/server/CMakeLists.txt @@ -209,7 +209,6 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dbustypeslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.h - ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.h ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxroutemonitor.h ) @@ -220,7 +219,6 @@ if(LINUX) ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxnetworkwatcherworker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/linuxdependencies.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/dnsutilslinux.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/pidtracker.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/iputilslinux.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/linuxdaemon.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../../client/platforms/linux/daemon/wireguardutilslinux.cpp diff --git a/service/server/router_linux.cpp b/service/server/router_linux.cpp index d717ce9c..9410b146 100644 --- a/service/server/router_linux.cpp +++ b/service/server/router_linux.cpp @@ -14,17 +14,6 @@ #include #include #include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include RouterLinux &RouterLinux::Instance() @@ -33,123 +22,6 @@ RouterLinux &RouterLinux::Instance() return s; } -#define BUFFER_SIZE 4096 - -QString RouterLinux::getgatewayandiface() -{ - int received_bytes = 0, msg_len = 0, route_attribute_len = 0; - int sock = -1, msgseq = 0; - struct nlmsghdr *nlh, *nlmsg; - struct rtmsg *route_entry; - // This struct contain route attributes (route type) - struct rtattr *route_attribute; - char gateway_address[INET_ADDRSTRLEN], interface[IF_NAMESIZE]; - char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; - char *ptr = buffer; - struct timeval tv; - - if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { - perror("socket failed"); - return ""; - } - - memset(msgbuf, 0, sizeof(msgbuf)); - memset(gateway_address, 0, sizeof(gateway_address)); - memset(interface, 0, sizeof(interface)); - memset(buffer, 0, sizeof(buffer)); - - /* point the header and the msg structure pointers into the buffer */ - nlmsg = (struct nlmsghdr *)msgbuf; - - /* Fill in the nlmsg header*/ - nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . - nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. - nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. - nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. - - /* 1 Sec Timeout to avoid stall */ - tv.tv_sec = 1; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); - /* send msg */ - if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { - perror("send failed"); - return ""; - } - - /* receive response */ - do - { - received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); - if (received_bytes < 0) { - perror("Error in recv"); - return ""; - } - - nlh = (struct nlmsghdr *) ptr; - - /* Check if the header is valid */ - if((NLMSG_OK(nlmsg, received_bytes) == 0) || - (nlmsg->nlmsg_type == NLMSG_ERROR)) - { - perror("Error in received packet"); - return ""; - } - - /* If we received all data break */ - if (nlh->nlmsg_type == NLMSG_DONE) - break; - else { - ptr += received_bytes; - msg_len += received_bytes; - } - - /* Break if its not a multi part message */ - if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) - break; - } - while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); - - /* parse response */ - for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) - { - /* Get the route data */ - route_entry = (struct rtmsg *) NLMSG_DATA(nlh); - - /* We are just interested in main routing table */ - if (route_entry->rtm_table != RT_TABLE_MAIN) - continue; - - route_attribute = (struct rtattr *) RTM_RTA(route_entry); - route_attribute_len = RTM_PAYLOAD(nlh); - - /* Loop through all attributes */ - for ( ; RTA_OK(route_attribute, route_attribute_len); - route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) - { - switch(route_attribute->rta_type) { - case RTA_OIF: - if_indextoname(*(int *)RTA_DATA(route_attribute), interface); - break; - case RTA_GATEWAY: - inet_ntop(AF_INET, RTA_DATA(route_attribute), - gateway_address, sizeof(gateway_address)); - break; - default: - break; - } - } - - if ((*gateway_address) && (*interface)) { - qDebug().noquote() << "Gateway " << gateway_address << " for interface " << interface; - break; - } - } - close(sock); - return gateway_address; -} - - bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const int &sock) { QString ip = Utils::ipAddressFromIpWithSubnet(ipWithSubnet); @@ -157,7 +29,7 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to add invalid route: " << ip << gw; - return true; + return false; } struct rtentry route; @@ -181,11 +53,11 @@ bool RouterLinux::routeAdd(const QString &ipWithSubnet, const QString &gw, const if (int err = ioctl(sock, SIOCADDRT, &route) < 0) { - // qDebug().noquote() << "route add error: gw " - // << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr - // << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr - // << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; - // return false; + qDebug().noquote() << "route add error: gw " + << ((struct sockaddr_in *)&route.rt_gateway)->sin_addr.s_addr + << " ip " << ((struct sockaddr_in *)&route.rt_dst)->sin_addr.s_addr + << " mask " << ((struct sockaddr_in *)&route.rt_genmask)->sin_addr.s_addr << " " << err; + return false; } m_addedRoutes.append({ipWithSubnet, gw}); @@ -227,7 +99,7 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (!Utils::checkIPv4Format(ip) || !Utils::checkIPv4Format(gw)) { qCritical().noquote() << "Critical, trying to remove invalid route: " << ip << gw; - return true; + return false; } if (ip == "0.0.0.0") { @@ -257,8 +129,8 @@ bool RouterLinux::routeDelete(const QString &ipWithSubnet, const QString &gw, co if (ioctl(sock, SIOCDELRT, &route) < 0) { - // qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; - // return false; + qDebug().noquote() << "route delete error: gw " << gw << " ip " << ip << " mask " << mask; + return false; } return true; } From 52e5453d56f8a024c2a9b54a4cef75ac99494ddc Mon Sep 17 00:00:00 2001 From: Mykola Baibuz Date: Wed, 20 Sep 2023 14:27:28 -0400 Subject: [PATCH 4/6] Upload AWG binary --- client/3rd-prebuilt | 2 +- client/daemon/daemonlocalserver.cpp | 4 ++-- client/mozilla/shared/ipaddress.cpp | 2 -- client/platforms/linux/daemon/wireguardutilslinux.cpp | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/client/3rd-prebuilt b/client/3rd-prebuilt index bc450df2..e8795854 160000 --- a/client/3rd-prebuilt +++ b/client/3rd-prebuilt @@ -1 +1 @@ -Subproject commit bc450df2418540869e97a31c4dd76839989b1fa6 +Subproject commit e8795854a5cf27004fe78caecc90a961688d1d41 diff --git a/client/daemon/daemonlocalserver.cpp b/client/daemon/daemonlocalserver.cpp index 9d8feb68..9b8e9504 100644 --- a/client/daemon/daemonlocalserver.cpp +++ b/client/daemon/daemonlocalserver.cpp @@ -77,12 +77,12 @@ QString DaemonLocalServer::daemonPath() const { } if (dir.exists("amneziavpn")) { - logger.debug() << "/var/run/amnezia seems to be usable"; + logger.debug() << "/var/run/amneziavpn seems to be usable"; return VAR_PATH; } if (!dir.mkdir("amneziavpn")) { - logger.warning() << "Failed to create /var/run/amnezia"; + logger.warning() << "Failed to create /var/run/amneziavpn"; return TMP_PATH; } diff --git a/client/mozilla/shared/ipaddress.cpp b/client/mozilla/shared/ipaddress.cpp index 1f84ad07..95a2f2d0 100644 --- a/client/mozilla/shared/ipaddress.cpp +++ b/client/mozilla/shared/ipaddress.cpp @@ -29,8 +29,6 @@ IPAddress::IPAddress(const QString& ip) { if (m_prefixLength >= 128) { m_prefixLength = 128; } - } else { - // Q_ASSERT(false); } } diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp index 32150ad5..a8b7b04a 100644 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ b/client/platforms/linux/daemon/wireguardutilslinux.cpp @@ -76,7 +76,7 @@ bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { QDir appPath(QCoreApplication::applicationDirPath()); QStringList wgArgs = {"-f", "amn0"}; - m_tunnel.start(appPath.filePath("wireguard-go"), wgArgs); + m_tunnel.start(appPath.filePath("../../client/bin/wireguard-go"), wgArgs); if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) { logger.error() << "Unable to start tunnel process due to timeout"; m_tunnel.kill(); From 97a72a9ee2d2041cb858b036909e12a3f206fa19 Mon Sep 17 00:00:00 2001 From: pokamest Date: Thu, 21 Sep 2023 11:28:18 +0100 Subject: [PATCH 5/6] build_windows.bat fix --- deploy/PrivacyTechAppleCertDeveloperId.p12 | Bin 3336 -> 0 bytes deploy/PrivacyTechAppleCertInstallerId.p12 | Bin 3332 -> 0 bytes deploy/PrivacyTechWindowsCert.pfx | Bin 9546 -> 0 bytes deploy/WWDRCA.cer | Bin 1062 -> 0 bytes deploy/build_windows.bat | 36 ++++++++------------- 5 files changed, 13 insertions(+), 23 deletions(-) delete mode 100755 deploy/PrivacyTechAppleCertDeveloperId.p12 delete mode 100755 deploy/PrivacyTechAppleCertInstallerId.p12 delete mode 100644 deploy/PrivacyTechWindowsCert.pfx delete mode 100644 deploy/WWDRCA.cer diff --git a/deploy/PrivacyTechAppleCertDeveloperId.p12 b/deploy/PrivacyTechAppleCertDeveloperId.p12 deleted file mode 100755 index a04ec85a02ac7e68cd42ccd951d286abb33fe7ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3336 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 diff --git a/deploy/PrivacyTechWindowsCert.pfx b/deploy/PrivacyTechWindowsCert.pfx deleted file mode 100644 index 42c49a339f02c9f69fbfcad55f92bf99830662b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9546 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 D2`7-6 diff --git a/deploy/build_windows.bat b/deploy/build_windows.bat index b6892f3d..7ae3e9f6 100644 --- a/deploy/build_windows.bat +++ b/deploy/build_windows.bat @@ -14,41 +14,31 @@ set PROJECT_DIR=%cd% set SCRIPT_DIR=%PROJECT_DIR:"=%\deploy set WORK_DIR=%SCRIPT_DIR:"=%\build_%BUILD_ARCH:"=% -rmdir /Q /S %WORK_DIR% -mkdir %WORK_DIR% - - set APP_NAME=AmneziaVPN set APP_FILENAME=%APP_NAME:"=%.exe set APP_DOMAIN=org.amneziavpn.package -set RELEASE_DIR=%WORK_DIR:"=% -set OUT_APP_DIR=%RELEASE_DIR:"=%\client\release -set PREBILT_DEPLOY_DATA_DIR=%SCRIPT_DIR:"=%\data\deploy-prebuilt\windows\x%BUILD_ARCH:"=% +set OUT_APP_DIR=%WORK_DIR:"=%\client\release +set PREBILT_DEPLOY_DATA_DIR=%PROJECT_DIR:"=%\client\3rd-prebuilt\deploy-prebuilt\windows\x%BUILD_ARCH:"=% set DEPLOY_DATA_DIR=%SCRIPT_DIR:"=%\data\windows\x%BUILD_ARCH:"=% -set INSTALLER_DATA_DIR=%RELEASE_DIR:"=%\installer\packages\%APP_DOMAIN:"=%\data +set INSTALLER_DATA_DIR=%WORK_DIR:"=%\installer\packages\%APP_DOMAIN:"=%\data set TARGET_FILENAME=%PROJECT_DIR:"=%\%APP_NAME:"=%_x%BUILD_ARCH:"=%.exe echo "Environment:" +echo "WORK_DIR: %WORK_DIR%" echo "APP_FILENAME: %APP_FILENAME%" echo "PROJECT_DIR: %PROJECT_DIR%" echo "SCRIPT_DIR: %SCRIPT_DIR%" -echo "RELEASE_DIR: %RELEASE_DIR%" echo "OUT_APP_DIR: %OUT_APP_DIR%" echo "DEPLOY_DATA_DIR: %DEPLOY_DATA_DIR%" echo "INSTALLER_DATA_DIR: %INSTALLER_DATA_DIR%" -echo "QMAKE_STASH_FILE: %QMAKE_STASH_FILE%" echo "TARGET_FILENAME: %TARGET_FILENAME%" -rem Signing staff -powershell Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope LocalMachine -powershell Get-ExecutionPolicy -List - -powershell Import-PfxCertificate -FilePath %SCRIPT_DIR:"=%\PrivacyTechWindowsCert.pfx -CertStoreLocation Cert:\LocalMachine\My -Password $(ConvertTo-SecureString -String $Env:WIN_CERT_PW -AsPlainText -Force) - echo "Cleanup..." -Rmdir /Q /S %RELEASE_DIR% +rmdir /Q /S %WORK_DIR% Del %TARGET_FILENAME% +mkdir %WORK_DIR% + call "%QT_BIN_DIR:"=%\qt-cmake" --version "%QT_BIN_DIR:"=%\windeployqt" -v cmake --version @@ -69,11 +59,11 @@ copy "%WORK_DIR:"=%\service\server\release\%APP_NAME:"=%-service.exe" %OUT_APP_D echo "Signing exe" cd %OUT_APP_DIR% -signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.exe - +signtool sign /v /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.exe + "%QT_BIN_DIR:"=%\windeployqt" --release --qmldir "%PROJECT_DIR:"=%\client" --force --no-translations "%OUT_APP_DIR:"=%\%APP_FILENAME:"=%" -signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.dll +signtool sign /v /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 *.dll echo "Copying deploy data..." xcopy %DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f @@ -81,7 +71,7 @@ xcopy %PREBILT_DEPLOY_DATA_DIR% %OUT_APP_DIR% /s /e /y /i /f copy "%WORK_DIR:"=%\service\wireguard-service\release\wireguard-service.exe" %OUT_APP_DIR%\wireguard\ cd %SCRIPT_DIR% -xcopy %SCRIPT_DIR:"=%\installer %RELEASE_DIR:"=%\installer /s /e /y /i /f +xcopy %SCRIPT_DIR:"=%\installer %WORK_DIR:"=%\installer /s /e /y /i /f mkdir %INSTALLER_DATA_DIR% echo "Deploy finished, content:" @@ -91,14 +81,14 @@ cd %OUT_APP_DIR% echo "Compressing data..." "%QIF_BIN_DIR:"=%\archivegen" -c 9 %INSTALLER_DATA_DIR:"=%\%APP_NAME:"=%.7z . -cd "%RELEASE_DIR:"=%\installer" +cd "%WORK_DIR:"=%\installer" echo "Creating installer..." "%QIF_BIN_DIR:"=%\binarycreator" --offline-only -v -c config\windows.xml -p packages -f %TARGET_FILENAME% timeout 5 cd %PROJECT_DIR% -signtool sign /v /sm /s My /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 "%TARGET_FILENAME%" +signtool sign /v /n "Privacy Technologies OU" /fd sha256 /tr http://timestamp.comodoca.com/?td=sha256 /td sha256 "%TARGET_FILENAME%" echo "Finished, see %TARGET_FILENAME%" exit 0 From 665f2412f1fdab94f18e3d21e01c0f73802517c1 Mon Sep 17 00:00:00 2001 From: pokamest Date: Thu, 21 Sep 2023 05:14:15 -0700 Subject: [PATCH 6/6] Version bump, macos/ios build fix [no ci] --- CMakeLists.txt | 4 ++-- deploy/build_ios.sh | 2 +- deploy/build_macos.sh | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ecde0cd..f6f94a23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,11 +2,11 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR) set(PROJECT AmneziaVPN) -project(${PROJECT} VERSION 3.1.0.0 +project(${PROJECT} VERSION 3.1.0.1 DESCRIPTION "AmneziaVPN" HOMEPAGE_URL "https://amnezia.org/" ) -set(RELEASE_DATE "2023-08-28") +set(RELEASE_DATE "2023-09-21") set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}) if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") diff --git a/deploy/build_ios.sh b/deploy/build_ios.sh index c4c0691a..7f16b916 100755 --- a/deploy/build_ios.sh +++ b/deploy/build_ios.sh @@ -34,7 +34,7 @@ clang -v # Generate XCodeProj $QT_BIN_DIR/qt-cmake . -B $BUILD_DIR -GXcode -DQT_HOST_PATH=$QT_MACOS_ROOT_DIR -KEYCHAIN=amnezia.build.keychain +KEYCHAIN=amnezia.build.ios.keychain KEYCHAIN_FILE=$HOME/Library/Keychains/${KEYCHAIN}-db # Setup keychain diff --git a/deploy/build_macos.sh b/deploy/build_macos.sh index 54b6dbe3..700198e7 100755 --- a/deploy/build_macos.sh +++ b/deploy/build_macos.sh @@ -79,7 +79,7 @@ if [ "${MAC_CERT_PW+x}" ]; then CERTIFICATE_P12=$DEPLOY_DIR/PrivacyTechAppleCertDeveloperId.p12 WWDRCA=$DEPLOY_DIR/WWDRCA.cer - KEYCHAIN=amnezia.build.keychain + KEYCHAIN=amnezia.build.macos.keychain TEMP_PASS=tmp_pass security create-keychain -p $TEMP_PASS $KEYCHAIN || true