diff --git a/client/platforms/linux/daemon/apptracker.cpp b/client/platforms/linux/daemon/apptracker.cpp deleted file mode 100644 index 437970cd..00000000 --- a/client/platforms/linux/daemon/apptracker.cpp +++ /dev/null @@ -1,109 +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 "apptracker.h" -#include "dbustypeslinux.h" -#include "leakdetector.h" -#include "logger.h" - -#include -#include -#include -#include - -#include - -constexpr const char* GTK_DESKTOP_APP_SERVICE = "org.gtk.gio.DesktopAppInfo"; -constexpr const char* GTK_DESKTOP_APP_PATH = "/org/gtk/gio/DesktopAppInfo"; - -constexpr const char* DBUS_LOGIN_SERVICE = "org.freedesktop.login1"; -constexpr const char* DBUS_LOGIN_PATH = "/org/freedesktop/login1"; -constexpr const char* DBUS_LOGIN_MANAGER = "org.freedesktop.login1.Manager"; - -namespace { -Logger logger(LOG_LINUX, "AppTracker"); -} - -AppTracker::AppTracker(QObject* parent) : QObject(parent) { - MVPN_COUNT_CTOR(AppTracker); - logger.debug() << "AppTracker created."; - - QDBusConnection m_conn = QDBusConnection::systemBus(); - m_conn.connect("", DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER, "UserNew", this, - SLOT(userCreated(uint, const QDBusObjectPath&))); - m_conn.connect("", DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER, "UserRemoved", this, - SLOT(userRemoved(uint, const QDBusObjectPath&))); - - QDBusInterface n(DBUS_LOGIN_SERVICE, DBUS_LOGIN_PATH, DBUS_LOGIN_MANAGER, - m_conn); - QDBusPendingReply reply = n.asyncCall("ListUsers"); - QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this); - QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, - SLOT(userListCompleted(QDBusPendingCallWatcher*))); -} - -AppTracker::~AppTracker() { - MVPN_COUNT_DTOR(AppTracker); - logger.debug() << "AppTracker destroyed."; -} - -void AppTracker::userListCompleted(QDBusPendingCallWatcher* watcher) { - QDBusPendingReply reply = *watcher; - if (reply.isValid()) { - UserDataList list = reply.value(); - for (auto user : list) { - userCreated(user.userid, user.path); - } - } - - delete watcher; -} - -void AppTracker::userCreated(uint userid, const QDBusObjectPath& path) { - logger.debug() << "User created uid:" << userid << "at:" << path.path(); - - /* Acquire the effective UID of the user to connect to their session bus. */ - uid_t realuid = getuid(); - if (seteuid(userid) < 0) { - logger.warning() << "Failed to set effective UID"; - } - auto guard = qScopeGuard([&] { - if (seteuid(realuid) < 0) { - logger.warning() << "Failed to restore effective UID"; - } - }); - - /* For correctness we should ask systemd for the user's runtime directory. */ - QString busPath = "unix:path=/run/user/" + QString::number(userid) + "/bus"; - logger.debug() << "Connection to" << busPath; - QDBusConnection connection = - QDBusConnection::connectToBus(busPath, "user-" + QString::number(userid)); - - /* Connect to the user's GTK launch event. */ - bool isConnected = connection.connect( - "", GTK_DESKTOP_APP_PATH, GTK_DESKTOP_APP_SERVICE, "Launched", this, - SLOT(gtkLaunchEvent(const QByteArray&, const QString&, qlonglong, - const QStringList&, const QVariantMap&))); - if (!isConnected) { - logger.warning() << "Failed to connect to GTK Launched signal"; - } -} - -void AppTracker::userRemoved(uint uid, const QDBusObjectPath& path) { - logger.debug() << "User removed uid:" << uid << "at:" << path.path(); - QDBusConnection::disconnectFromBus("user-" + QString::number(uid)); -} - -void AppTracker::gtkLaunchEvent(const QByteArray& appid, const QString& display, - qlonglong pid, const QStringList& uris, - const QVariantMap& extra) { - Q_UNUSED(display); - Q_UNUSED(uris); - Q_UNUSED(extra); - - QString appIdName(appid); - if (!appIdName.isEmpty()) { - emit appLaunched(appIdName, pid); - } -} diff --git a/client/platforms/linux/daemon/apptracker.h b/client/platforms/linux/daemon/apptracker.h deleted file mode 100644 index 01d6b9b4..00000000 --- a/client/platforms/linux/daemon/apptracker.h +++ /dev/null @@ -1,31 +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 APPTRACKER_H -#define APPTRACKER_H - -#include -#include - -class AppTracker final : public QObject { - Q_OBJECT - Q_DISABLE_COPY_MOVE(AppTracker) - - public: - explicit AppTracker(QObject* parent); - ~AppTracker(); - - signals: - void appLaunched(const QString& name, int rootpid); - - private slots: - void userListCompleted(QDBusPendingCallWatcher* call); - void userCreated(uint uid, const QDBusObjectPath& path); - void userRemoved(uint uid, const QDBusObjectPath& path); - void gtkLaunchEvent(const QByteArray& appid, const QString& display, - qlonglong pid, const QStringList& uris, - const QVariantMap& extra); -}; - -#endif // APPTRACKER_H diff --git a/client/platforms/linux/daemon/dbusservice.cpp b/client/platforms/linux/daemon/dbusservice.cpp deleted file mode 100644 index 3219b5be..00000000 --- a/client/platforms/linux/daemon/dbusservice.cpp +++ /dev/null @@ -1,249 +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 "dbusservice.h" -#include "dbus_adaptor.h" -#include "leakdetector.h" -#include "logger.h" -#include "loghandler.h" -#include "polkithelper.h" - -#include -#include -#include - -namespace { -Logger logger(LOG_LINUX, "DBusService"); -} - -constexpr const char* APP_STATE_ACTIVE = "active"; -constexpr const char* APP_STATE_EXCLUDED = "excluded"; -constexpr const char* APP_STATE_BLOCKED = "blocked"; - -DBusService::DBusService(QObject* parent) : Daemon(parent) { - MVPN_COUNT_CTOR(DBusService); - - m_wgutils = new WireguardUtilsLinux(this); - m_apptracker = new AppTracker(this); - m_pidtracker = new PidTracker(this); - - connect(m_apptracker, SIGNAL(appLaunched(const QString&, int)), this, - SLOT(appLaunched(const QString&, int))); - connect(m_pidtracker, SIGNAL(terminated(const QString&, int)), this, - SLOT(appTerminated(const QString&, int))); - - if (!removeInterfaceIfExists()) { - qFatal("Interface `%s` exists and cannot be removed. Cannot proceed!", - WG_INTERFACE); - } -} - -DBusService::~DBusService() { MVPN_COUNT_DTOR(DBusService); } - -IPUtils* DBusService::iputils() { - if (!m_iputils) { - m_iputils = new IPUtilsLinux(this); - } - return m_iputils; -} - -DnsUtils* DBusService::dnsutils() { - if (!m_dnsutils) { - m_dnsutils = new DnsUtilsLinux(this); - } - return m_dnsutils; -} - -void DBusService::setAdaptor(DbusAdaptor* adaptor) { - Q_ASSERT(!m_adaptor); - m_adaptor = adaptor; -} - -bool DBusService::removeInterfaceIfExists() { - if (m_wgutils->interfaceExists()) { - logger.warning() << "Device already exists. Let's remove it."; - if (!m_wgutils->deleteInterface()) { - logger.error() << "Failed to remove the device."; - return false; - } - } - return true; -} - -QString DBusService::version() { - logger.debug() << "Version request"; - return PROTOCOL_VERSION; -} - -bool DBusService::activate(const QString& jsonConfig) { - logger.debug() << "Activate"; - - if (!PolkitHelper::instance()->checkAuthorization( - "org.mozilla.vpn.activate")) { - logger.error() << "Polkit rejected"; - return false; - } - - QJsonDocument json = QJsonDocument::fromJson(jsonConfig.toLocal8Bit()); - if (!json.isObject()) { - logger.error() << "Invalid input"; - return false; - } - - QJsonObject obj = json.object(); - - InterfaceConfig config; - if (!parseConfig(obj, config)) { - logger.error() << "Invalid configuration"; - return false; - } - - if (obj.contains("vpnDisabledApps")) { - QJsonArray disabledApps = obj["vpnDisabledApps"].toArray(); - for (const QJsonValue& app : disabledApps) { - firewallApp(app.toString(), APP_STATE_EXCLUDED); - } - } - - return Daemon::activate(config); -} - -bool DBusService::deactivate(bool emitSignals) { - logger.debug() << "Deactivate"; - firewallClear(); - return Daemon::deactivate(emitSignals); -} - -QString DBusService::status() { return QString(getStatus()); } - -QByteArray DBusService::getStatus() { - logger.debug() << "Status request"; - QJsonObject json; - - if (!m_connections.contains(0)) { - json.insert("status", QJsonValue(false)); - return QJsonDocument(json).toJson(QJsonDocument::Compact); - } - - const InterfaceConfig& config = m_connections.value(0).m_config; - if (!m_wgutils->interfaceExists()) { - logger.error() << "Unable to get device"; - json.insert("status", QJsonValue(false)); - return QJsonDocument(json).toJson(QJsonDocument::Compact); - } - - json.insert("status", QJsonValue(true)); - json.insert("serverIpv4Gateway", QJsonValue(config.m_serverIpv4Gateway)); - json.insert("deviceIpv4Address", QJsonValue(config.m_deviceIpv4Address)); - WireguardUtilsLinux::peerStatus status = - m_wgutils->getPeerStatus(config.m_serverPublicKey); - json.insert("txBytes", QJsonValue(status.txBytes)); - json.insert("rxBytes", QJsonValue(status.rxBytes)); - - return QJsonDocument(json).toJson(QJsonDocument::Compact); -} - -QString DBusService::getLogs() { - logger.debug() << "Log request"; - return Daemon::logs(); -} - -void DBusService::appLaunched(const QString& name, int rootpid) { - logger.debug() << "tracking:" << name << "PID:" << rootpid; - ProcessGroup* group = m_pidtracker->track(name, rootpid); - if (m_firewallApps.contains(name)) { - group->state = m_firewallApps[name]; - group->moveToCgroup(getAppStateCgroup(group->state)); - } -} - -void DBusService::appTerminated(const QString& name, int rootpid) { - logger.debug() << "terminate:" << name << "PID:" << rootpid; -} - -/* Get the list of running applications that the firewall knows about. */ -QString DBusService::runningApps() { - QJsonArray result; - for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) { - const ProcessGroup* group = *i; - QJsonObject appObject; - QJsonArray pidList; - appObject.insert("name", QJsonValue(group->name)); - appObject.insert("rootpid", QJsonValue(group->rootpid)); - appObject.insert("state", QJsonValue(group->state)); - - for (auto pid : group->kthreads.keys()) { - pidList.append(QJsonValue(pid)); - } - - appObject.insert("pids", pidList); - result.append(appObject); - } - - return QJsonDocument(result).toJson(QJsonDocument::Compact); -} - -/* Update the firewall for running applications matching the application ID. */ -bool DBusService::firewallApp(const QString& appName, const QString& state) { - logger.debug() << "Setting" << appName << "to firewall state" << state; - m_firewallApps[appName] = state; - QString cgroup = getAppStateCgroup(state); - - /* Change matching applications' state to excluded */ - for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) { - ProcessGroup* group = *i; - if (group->name != appName) { - continue; - } - group->state = state; - group->moveToCgroup(cgroup); - } - - return true; -} - -/* Update the firewall for the application matching the desired PID. */ -bool DBusService::firewallPid(int rootpid, const QString& state) { - ProcessGroup* group = m_pidtracker->group(rootpid); - if (!group) { - return false; - } - - group->state = state; - group->moveToCgroup(getAppStateCgroup(group->state)); - - logger.debug() << "Setting" << group->name << "PID:" << rootpid - << "to firewall state" << state; - return true; -} - -/* Clear the firewall and return all applications to the active state */ -bool DBusService::firewallClear() { - const QString cgroup = getAppStateCgroup(APP_STATE_ACTIVE); - - m_firewallApps.clear(); - for (auto i = m_pidtracker->begin(); i != m_pidtracker->end(); i++) { - ProcessGroup* group = *i; - if (group->state == APP_STATE_ACTIVE) { - continue; - } - - group->state = APP_STATE_ACTIVE; - group->moveToCgroup(cgroup); - - logger.debug() << "Setting" << group->name << "PID:" << group->rootpid - << "to firewall state" << group->state; - } - return true; -} - -QString DBusService::getAppStateCgroup(const QString& state) { - if (state == APP_STATE_EXCLUDED) { - return m_wgutils->getExcludeCgroup(); - } - if (state == APP_STATE_BLOCKED) { - return m_wgutils->getBlockCgroup(); - } - return m_wgutils->getDefaultCgroup(); -} diff --git a/client/platforms/linux/daemon/dbusservice.h b/client/platforms/linux/daemon/dbusservice.h deleted file mode 100644 index c5a80314..00000000 --- a/client/platforms/linux/daemon/dbusservice.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 DBUSSERVICE_H -#define DBUSSERVICE_H - -#include "daemon/daemon.h" -#include "apptracker.h" -#include "iputilslinux.h" -#include "dnsutilslinux.h" -#include "pidtracker.h" -#include "wireguardutilslinux.h" - -class DbusAdaptor; - -class DBusService final : public Daemon { - Q_OBJECT - Q_DISABLE_COPY_MOVE(DBusService) - Q_CLASSINFO("D-Bus Interface", "org.mozilla.vpn.dbus") - - public: - DBusService(QObject* parent); - ~DBusService(); - - void setAdaptor(DbusAdaptor* adaptor); - - using Daemon::activate; - - public slots: - bool activate(const QString& jsonConfig); - - bool deactivate(bool emitSignals = true) override; - QString status(); - - QString version(); - QString getLogs(); - - QString runningApps(); - bool firewallApp(const QString& appName, const QString& state); - bool firewallPid(int rootpid, const QString& state); - bool firewallClear(); - - protected: - WireguardUtils* wgutils() const override { return m_wgutils; } - bool supportIPUtils() const override { return true; } - IPUtils* iputils() override; - bool supportDnsUtils() const override { return true; } - DnsUtils* dnsutils() override; - - QByteArray getStatus() override; - - private: - bool removeInterfaceIfExists(); - QString getAppStateCgroup(const QString& state); - - private slots: - void appLaunched(const QString& name, int rootpid); - void appTerminated(const QString& name, int rootpid); - - private: - DbusAdaptor* m_adaptor = nullptr; - WireguardUtilsLinux* m_wgutils = nullptr; - IPUtilsLinux* m_iputils = nullptr; - DnsUtilsLinux* m_dnsutils = nullptr; - - AppTracker* m_apptracker = nullptr; - PidTracker* m_pidtracker = nullptr; - QMap m_firewallApps; -}; - -#endif // DBUSSERVICE_H diff --git a/client/platforms/linux/daemon/dbustypeslinux.h b/client/platforms/linux/daemon/dbustypeslinux.h deleted file mode 100644 index 5489149a..00000000 --- a/client/platforms/linux/daemon/dbustypeslinux.h +++ /dev/null @@ -1,170 +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 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 deleted file mode 100644 index 214b4611..00000000 --- a/client/platforms/linux/daemon/dnsutilslinux.cpp +++ /dev/null @@ -1,206 +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 "dnsutilslinux.h" -#include "leakdetector.h" -#include "logger.h" - -#include -#include - -#include - -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(LOG_LINUX, "DnsUtilsLinux"); -} - -DnsUtilsLinux::DnsUtilsLinux(QObject* parent) : DnsUtils(parent) { - MVPN_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() { - MVPN_COUNT_DTOR(DnsUtilsLinux); - - for (int ifindex : m_linkDomains.keys()) { - QList argumentList; - argumentList << QVariant::fromValue(ifindex); - argumentList << QVariant::fromValue(m_linkDomains[ifindex]); - 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 ifindex : m_linkDomains.keys()) { - setLinkDomains(ifindex, m_linkDomains[ifindex]); - } - 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 (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 (auto d : domains) { - logger.debug() << "Setting DNS domain:" << 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 (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 ifindex : m_linkDomains.keys()) { - if (!m_linkDomains[ifindex].contains(root)) { - continue; - } - QList newlist = m_linkDomains[ifindex]; - newlist.removeAll(root); - setLinkDomains(ifindex, 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 deleted file mode 100644 index 67c6f698..00000000 --- a/client/platforms/linux/daemon/dnsutilslinux.h +++ /dev/null @@ -1,41 +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 DNSUTILSLINUX_H -#define DNSUTILSLINUX_H - -#include "daemon/dnsutils.h" -#include "dbustypeslinux.h" - -#include -#include - -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 deleted file mode 100644 index 6d79d35a..00000000 --- a/client/platforms/linux/daemon/iputilslinux.cpp +++ /dev/null @@ -1,150 +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 "iputilslinux.h" - -#include "daemon/wireguardutils.h" -#include "leakdetector.h" -#include "logger.h" - -#include -#include -#include -#include - -#include -#include - -constexpr uint32_t ETH_MTU = 1500; -constexpr uint32_t WG_MTU_OVERHEAD = 80; - -namespace { -Logger logger(LOG_LINUX, "IPUtilsLinux"); -} - -IPUtilsLinux::IPUtilsLinux(QObject* parent) : IPUtils(parent) { - MVPN_COUNT_CTOR(IPUtilsLinux); - logger.debug() << "IPUtilsLinux created."; -} - -IPUtilsLinux::~IPUtilsLinux() { - MVPN_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: " << 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: " << deviceAddr - << "error:" << strerror(errno); - return false; - } - - return true; -} diff --git a/client/platforms/linux/daemon/iputilslinux.h b/client/platforms/linux/daemon/iputilslinux.h deleted file mode 100644 index d10f70d7..00000000 --- a/client/platforms/linux/daemon/iputilslinux.h +++ /dev/null @@ -1,31 +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 IPUTILSLINUX_H -#define IPUTILSLINUX_H - -#include "daemon/iputils.h" - -#include - -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 deleted file mode 100644 index 2a3026b6..00000000 --- a/client/platforms/linux/daemon/linuxdaemon.cpp +++ /dev/null @@ -1,58 +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 "command.h" -#include "dbusservice.h" -#include "dbus_adaptor.h" -#include "leakdetector.h" -#include "logger.h" -#include "loghandler.h" -#include "signalhandler.h" - -namespace { -Logger logger(LOG_LINUX, "main"); -} - -class CommandLinuxDaemon final : public Command { - public: - explicit CommandLinuxDaemon(QObject* parent) - : Command(parent, "linuxdaemon", "Starts the linux daemon") { - MVPN_COUNT_CTOR(CommandLinuxDaemon); - } - - ~CommandLinuxDaemon() { MVPN_COUNT_DTOR(CommandLinuxDaemon); } - - int run(QStringList& tokens) override { - Q_ASSERT(!tokens.isEmpty()); - LogHandler::setLocation("/var/log"); - - return runCommandLineApp([&]() { - DBusService* dbus = new DBusService(qApp); - DbusAdaptor* adaptor = new DbusAdaptor(dbus); - dbus->setAdaptor(adaptor); - - QDBusConnection connection = QDBusConnection::systemBus(); - logger.debug() << "Connecting to DBus..."; - - if (!connection.registerService("org.mozilla.vpn.dbus") || - !connection.registerObject("/", dbus)) { - logger.error() << "Connection failed - name:" - << connection.lastError().name() - << "message:" << connection.lastError().message(); - return 1; - } - - SignalHandler sh; - QObject::connect(&sh, &SignalHandler::quitRequested, [&]() { - dbus->deactivate(); - qApp->quit(); - }); - - logger.debug() << "Ready!"; - return qApp->exec(); - }); - } -}; - -static Command::RegistrationProxy s_commandLinuxDaemon; diff --git a/client/platforms/linux/daemon/org.mozilla.vpn.conf b/client/platforms/linux/daemon/org.mozilla.vpn.conf deleted file mode 100644 index 83b8ad4d..00000000 --- a/client/platforms/linux/daemon/org.mozilla.vpn.conf +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/platforms/linux/daemon/org.mozilla.vpn.dbus.service b/client/platforms/linux/daemon/org.mozilla.vpn.dbus.service deleted file mode 100644 index 700de193..00000000 --- a/client/platforms/linux/daemon/org.mozilla.vpn.dbus.service +++ /dev/null @@ -1,5 +0,0 @@ -[D-BUS Service] -User=root -Name=org.mozilla.vpn.dbus -Exec=/usr/bin/mozillavpn linuxdaemon -SystemdService=mozillavpn.service diff --git a/client/platforms/linux/daemon/org.mozilla.vpn.dbus.xml b/client/platforms/linux/daemon/org.mozilla.vpn.dbus.xml deleted file mode 100644 index ad3e6ba6..00000000 --- a/client/platforms/linux/daemon/org.mozilla.vpn.dbus.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/platforms/linux/daemon/org.mozilla.vpn.policy b/client/platforms/linux/daemon/org.mozilla.vpn.policy deleted file mode 100644 index e29679ca..00000000 --- a/client/platforms/linux/daemon/org.mozilla.vpn.policy +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Activate the Mozilla VPN - Activate the Mozilla VPN - - no - auth_admin - - - - - Deactivate the Mozilla VPN - Deactivate the Mozilla VPN - - no - auth_admin - - - - diff --git a/client/platforms/linux/daemon/pidtracker.cpp b/client/platforms/linux/daemon/pidtracker.cpp deleted file mode 100644 index 68f5a090..00000000 --- a/client/platforms/linux/daemon/pidtracker.cpp +++ /dev/null @@ -1,227 +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 "leakdetector.h" -#include "logger.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -constexpr size_t CN_MCAST_MSG_SIZE = - sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op); - -namespace { -Logger logger(LOG_LINUX, "PidTracker"); -} - -PidTracker::PidTracker(QObject* parent) : QObject(parent) { - MVPN_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() { - MVPN_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 pid : kthreads.keys()) { - fprintf(fp, "%d\n", pid); - 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 42b2313e..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") { - MVPN_COUNT_CTOR(ProcessGroup); - name = groupName; - rootpid = groupRootPid; - state = groupState; - refcount = 0; - } - ~ProcessGroup() { MVPN_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/polkithelper.cpp b/client/platforms/linux/daemon/polkithelper.cpp deleted file mode 100644 index 34838de0..00000000 --- a/client/platforms/linux/daemon/polkithelper.cpp +++ /dev/null @@ -1,66 +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 "polkithelper.h" - -#include - -// No extra QT includes after this line! -#undef Q_SIGNALS -#include "polkit/polkit.h" - -class Helper final { - public: - Helper() = default; - ~Helper() { - if (m_error) { - g_error_free(m_error); - } - - if (m_subject) { - g_object_unref(m_subject); - } - - if (m_result) { - g_object_unref(m_result); - } - } - - public: - GError* m_error = nullptr; - PolkitSubject* m_subject = nullptr; - PolkitAuthorizationResult* m_result = nullptr; -}; - -// static -PolkitHelper* PolkitHelper::instance() { - static PolkitHelper s_instance; - return &s_instance; -} - -bool PolkitHelper::checkAuthorization(const QString& actionId) { - qDebug() << "Check Authorization for" << actionId; - - Helper h; - - PolkitAuthority* authority = polkit_authority_get_sync(NULL, &h.m_error); - if (h.m_error) { - qDebug() << "Fail to generate a polkit authority object:" - << h.m_error->message; - return false; - } - - h.m_subject = polkit_unix_process_new_for_owner(getpid(), 0, -1); - - h.m_result = polkit_authority_check_authorization_sync( - authority, h.m_subject, actionId.toLatin1().data(), nullptr, - POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, nullptr, - &h.m_error); - if (h.m_error) { - qDebug() << "Authorization sync failed:" << h.m_error->message; - return false; - } - - return polkit_authorization_result_get_is_authorized(h.m_result); -} diff --git a/client/platforms/linux/daemon/polkithelper.h b/client/platforms/linux/daemon/polkithelper.h deleted file mode 100644 index 2be4f2b0..00000000 --- a/client/platforms/linux/daemon/polkithelper.h +++ /dev/null @@ -1,23 +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 POLKITHELPER_H -#define POLKITHELPER_H - -#include - -class PolkitHelper final { - public: - static PolkitHelper* instance(); - - bool checkAuthorization(const QString& actionId); - - private: - PolkitHelper() = default; - ~PolkitHelper() = default; - - Q_DISABLE_COPY(PolkitHelper) -}; - -#endif // POLKITHELPER_H diff --git a/client/platforms/linux/daemon/wireguardutilslinux.cpp b/client/platforms/linux/daemon/wireguardutilslinux.cpp deleted file mode 100644 index 1d090620..00000000 --- a/client/platforms/linux/daemon/wireguardutilslinux.cpp +++ /dev/null @@ -1,648 +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 "wireguardutilslinux.h" -#include "leakdetector.h" -#include "logger.h" -#include "platforms/linux/linuxdependencies.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// Import wireguard C library for Linux -#if defined(__cplusplus) -extern "C" { -#endif -#include "../../3rdparty/wireguard-tools/contrib/embeddable-wg-library/wireguard.h" -#include "../../linux/netfilter/netfilter.h" -#if defined(__cplusplus) -} -#endif -// End import wireguard - -/* Packets sent outside the VPN need to be marked for the routing policy - * to direct them appropriately. The value of the mark and the table ID - * aren't important, so long as they are unique. - */ -constexpr uint32_t WG_FIREWALL_MARK = 0xca6c; -constexpr uint32_t WG_ROUTE_TABLE = 0xca6c; - -/* Traffic classifiers can be used to mark packets which should be either - * excluded from the VPN tunnel, or blocked entirely. The values of these - * classifiers aren't important so long as they are unique. - */ -constexpr const char* VPN_EXCLUDE_CGROUP = "/mozvpn.exclude"; -constexpr const char* VPN_BLOCK_CGROUP = "/mozvpn.block"; -constexpr uint32_t VPN_EXCLUDE_CLASS_ID = 0x00110011; -constexpr uint32_t VPN_BLOCK_CLASS_ID = 0x00220022; - -static void nlmsg_append_attr(char* buf, size_t maxlen, int attrtype, - const void* attrdata, size_t attrlen); -static void nlmsg_append_attr32(char* buf, size_t maxlen, int attrtype, - uint32_t value); - -namespace { -Logger logger(LOG_LINUX, "WireguardUtilsLinux"); - -void NetfilterLogger(int level, const char* msg) { - Q_UNUSED(level); - logger.debug() << "NetfilterGo:" << msg; -} -} // namespace - -WireguardUtilsLinux::WireguardUtilsLinux(QObject* parent) - : WireguardUtils(parent) { - MVPN_COUNT_CTOR(WireguardUtilsLinux); - NetfilterSetLogger((GoUintptr)&NetfilterLogger); - NetfilterCreateTables(); - - m_nlsock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE); - if (m_nlsock < 0) { - logger.warning() << "Failed to create netlink socket:" << strerror(errno); - } - - 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_notifier = new QSocketNotifier(m_nlsock, QSocketNotifier::Read, this); - connect(m_notifier, &QSocketNotifier::activated, this, - &WireguardUtilsLinux::nlsockReady); - - /* Create control groups for split tunnelling */ - m_cgroups = LinuxDependencies::findCgroupPath("net_cls"); - if (!m_cgroups.isNull()) { - if (!setupCgroupClass(m_cgroups + VPN_EXCLUDE_CGROUP, - VPN_EXCLUDE_CLASS_ID)) { - m_cgroups.clear(); - } else if (!setupCgroupClass(m_cgroups + VPN_BLOCK_CGROUP, - VPN_BLOCK_CLASS_ID)) { - m_cgroups.clear(); - } - } - - logger.debug() << "WireguardUtilsLinux created."; -} - -WireguardUtilsLinux::~WireguardUtilsLinux() { - MVPN_COUNT_DTOR(WireguardUtilsLinux); - NetfilterRemoveTables(); - if (m_nlsock >= 0) { - close(m_nlsock); - } - logger.debug() << "WireguardUtilsLinux destroyed."; -} - -bool WireguardUtilsLinux::interfaceExists() { - // As currentInterfaces only gets wireguard interfaces, this method - // also confirms an interface as being a wireguard interface. - return currentInterfaces().contains(WG_INTERFACE); -}; - -bool WireguardUtilsLinux::addInterface(const InterfaceConfig& config) { - int code = wg_add_device(WG_INTERFACE); - if (code != 0) { - logger.error() << "Adding interface failed:" << strerror(-code); - return false; - } - - wg_device* device = static_cast(calloc(1, sizeof(*device))); - if (!device) { - logger.error() << "Allocation failure"; - return false; - } - auto guard = qScopeGuard([&] { wg_free_device(device); }); - - // Name - strncpy(device->name, WG_INTERFACE, IFNAMSIZ); - // Private Key - wg_key_from_base64(device->private_key, config.m_privateKey.toLocal8Bit()); - - // Set/update device - device->fwmark = WG_FIREWALL_MARK; - device->flags = (wg_device_flags)( - WGDEVICE_HAS_PRIVATE_KEY | WGDEVICE_REPLACE_PEERS | WGDEVICE_HAS_FWMARK); - if (wg_set_device(device) != 0) { - logger.error() << "Failed to setup the device"; - return false; - } - - // Create routing policy rules - if (!rtmSendRule(RTM_NEWRULE, - NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK, - AF_INET)) { - return false; - } - if (!rtmSendRule(RTM_NEWRULE, - NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK, - AF_INET6)) { - return false; - } - - // Configure firewall rules - GoString goIfname = {.p = device->name, .n = (ptrdiff_t)strlen(device->name)}; - if (NetfilterIfup(goIfname, device->fwmark) != 0) { - return false; - } - if (!m_cgroups.isNull()) { - NetfilterMarkCgroup(VPN_EXCLUDE_CLASS_ID, device->fwmark); - NetfilterBlockCgroup(VPN_BLOCK_CLASS_ID); - } - - int slashPos = config.m_deviceIpv6Address.indexOf('/'); - GoString goIpv6Address = {.p = qPrintable(config.m_deviceIpv6Address), - .n = config.m_deviceIpv6Address.length()}; - if (slashPos != -1) { - goIpv6Address.n = slashPos; - } - NetfilterIsolateIpv6(goIfname, goIpv6Address); - - return true; -} - -bool WireguardUtilsLinux::updatePeer(const InterfaceConfig& config) { - wg_device* device = static_cast(calloc(1, sizeof(*device))); - if (!device) { - logger.error() << "Allocation failure"; - return false; - } - auto guard = qScopeGuard([&] { wg_free_device(device); }); - - wg_peer* peer = static_cast(calloc(1, sizeof(*peer))); - if (!peer) { - logger.error() << "Allocation failure"; - return false; - } - device->first_peer = device->last_peer = peer; - - logger.debug() << "Adding peer" << printablePubkey(config.m_serverPublicKey); - - // Public Key - wg_key_from_base64(peer->public_key, qPrintable(config.m_serverPublicKey)); - // Endpoint - if (!setPeerEndpoint(&peer->endpoint.addr, config.m_serverIpv4AddrIn, - config.m_serverPort)) { - logger.error() << "Failed to set peer endpoint for hop" - << config.m_hopindex; - return false; - } - - // HACK: We are running into a crash on Linux due to the address list being - // *WAAAY* too long, which we aren't really using anways since the routing - // tables are doing all the work for us anyways. - // - // To work around the issue, just set default routes for hopindex zero. - if (config.m_hopindex == 0) { - if (!config.m_deviceIpv4Address.isNull()) { - addPeerPrefix(peer, IPAddressRange("0.0.0.0", 0, IPAddressRange::IPv4)); - } - if (!config.m_deviceIpv6Address.isNull()) { - addPeerPrefix(peer, IPAddressRange("::", 0, IPAddressRange::IPv6)); - } - } else { - for (const IPAddressRange& ip : config.m_allowedIPAddressRanges) { - bool ok = addPeerPrefix(peer, ip); - if (!ok) { - logger.error() << "Invalid IP address:" << ip.ipAddress(); - return false; - } - } - } - - // Set/update peer - strncpy(device->name, WG_INTERFACE, IFNAMSIZ); - device->flags = (wg_device_flags)0; - peer->flags = - (wg_peer_flags)(WGPEER_HAS_PUBLIC_KEY | WGPEER_REPLACE_ALLOWEDIPS); - if (wg_set_device(device) != 0) { - logger.error() << "Failed to set the new peer hop" << config.m_hopindex; - return false; - } - - return true; -} - -bool WireguardUtilsLinux::deletePeer(const QString& pubkey) { - wg_device* device = static_cast(calloc(1, sizeof(*device))); - if (!device) { - logger.error() << "Allocation failure"; - return false; - } - auto guard = qScopeGuard([&] { wg_free_device(device); }); - - wg_peer* peer = static_cast(calloc(1, sizeof(*peer))); - if (!peer) { - logger.error() << "Allocation failure"; - return false; - } - device->first_peer = device->last_peer = peer; - - logger.debug() << "Removing peer" << printablePubkey(pubkey); - - // Public Key - peer->flags = (wg_peer_flags)(WGPEER_HAS_PUBLIC_KEY | WGPEER_REMOVE_ME); - wg_key_from_base64(peer->public_key, qPrintable(pubkey)); - - // Set/update device - strncpy(device->name, WG_INTERFACE, IFNAMSIZ); - device->flags = (wg_device_flags)0; - if (wg_set_device(device) != 0) { - logger.error() << "Failed to remove the peer"; - return false; - } - - return true; -} - -bool WireguardUtilsLinux::deleteInterface() { - // Clear firewall rules - NetfilterClearTables(); - - // Clear routing policy rules - if (!rtmSendRule(RTM_DELRULE, NLM_F_REQUEST | NLM_F_ACK, AF_INET)) { - return false; - } - if (!rtmSendRule(RTM_DELRULE, NLM_F_REQUEST | NLM_F_ACK, AF_INET6)) { - return false; - } - - // Delete the interface - int returnCode = wg_del_device(WG_INTERFACE); - if (returnCode != 0) { - logger.error() << "Deleting interface failed:" << strerror(-returnCode); - return false; - } - - return true; -} - -WireguardUtils::peerStatus WireguardUtilsLinux::getPeerStatus( - const QString& pubkey) { - wg_device* device = nullptr; - wg_peer* peer = nullptr; - peerStatus status = {0, 0}; - - if (wg_get_device(&device, WG_INTERFACE) != 0) { - logger.warning() << "Unable to get stats for" << WG_INTERFACE; - return status; - } - - wg_key key; - wg_key_from_base64(key, qPrintable(pubkey)); - wg_for_each_peer(device, peer) { - if (memcmp(&key, &peer->public_key, sizeof(key)) != 0) { - continue; - } - status.txBytes = peer->tx_bytes; - status.rxBytes = peer->rx_bytes; - break; - } - wg_free_device(device); - return status; -} - -bool WireguardUtilsLinux::updateRoutePrefix(const IPAddressRange& prefix, - int hopindex) { - logger.debug() << "Adding route to" << prefix.toString(); - int flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE | NLM_F_ACK; - return rtmSendRoute(RTM_NEWROUTE, flags, prefix, hopindex); -} - -bool WireguardUtilsLinux::deleteRoutePrefix(const IPAddressRange& prefix, - int hopindex) { - logger.debug() << "Removing route to" << prefix.toString(); - int flags = NLM_F_REQUEST | NLM_F_ACK; - return rtmSendRoute(RTM_DELROUTE, flags, prefix, hopindex); -} - -bool WireguardUtilsLinux::rtmSendRoute(int action, int flags, - const IPAddressRange& prefix, - int hopindex) { - constexpr size_t rtm_max_size = sizeof(struct rtmsg) + - 2 * RTA_SPACE(sizeof(uint32_t)) + - RTA_SPACE(sizeof(struct in6_addr)); - int index = if_nametoindex(WG_INTERFACE); - if (index <= 0) { - logger.error() << "if_nametoindex() failed:" << strerror(errno); - return false; - } - - wg_allowedip ip; - if (!buildAllowedIp(&ip, prefix)) { - logger.warning() << "Invalid destination prefix"; - return false; - } - - char buf[NLMSG_SPACE(rtm_max_size)]; - struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; - struct rtmsg* rtm = (struct rtmsg*)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 = RTN_UNICAST; - rtm->rtm_protocol = RTPROT_BOOT; - rtm->rtm_scope = RT_SCOPE_UNIVERSE; - - // Routes for the main hop should be placed into their own table. - if (hopindex == 0) { - rtm->rtm_table = RT_TABLE_UNSPEC; - nlmsg_append_attr32(buf, sizeof(buf), RTA_TABLE, WG_ROUTE_TABLE); - } else { - rtm->rtm_table = RT_TABLE_MAIN; - } - - if (rtm->rtm_family == AF_INET6) { - nlmsg_append_attr(buf, sizeof(buf), RTA_DST, &ip.ip6, sizeof(ip.ip6)); - } else { - nlmsg_append_attr(buf, sizeof(buf), RTA_DST, &ip.ip4, sizeof(ip.ip4)); - } - nlmsg_append_attr32(buf, 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); -} - -// PRIVATE METHODS -QStringList WireguardUtilsLinux::currentInterfaces() { - char* deviceNames = wg_list_device_names(); - QStringList devices; - if (!deviceNames) { - return devices; - } - char* deviceName; - size_t len; - wg_for_each_device_name(deviceNames, deviceName, len) { - devices.append(deviceName); - } - free(deviceNames); - return devices; -} - -bool WireguardUtilsLinux::setPeerEndpoint(struct sockaddr* sa, - const QString& address, int port) { - QString portString = QString::number(port); - - struct addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_DGRAM; - hints.ai_protocol = IPPROTO_UDP; - - struct addrinfo* resolved = nullptr; - auto guard = qScopeGuard([&] { freeaddrinfo(resolved); }); - int retries = 15; - - for (unsigned int timeout = 1000000;; - timeout = std::min((unsigned int)20000000, timeout * 6 / 5)) { - int rv = getaddrinfo(address.toLocal8Bit(), portString.toLocal8Bit(), - &hints, &resolved); - if (!rv) { - break; - } - - /* The set of return codes that are "permanent failures". All other - * possibilities are potentially transient. - * - * This is according to https://sourceware.org/glibc/wiki/NameResolver which - * states: "From the perspective of the application that calls getaddrinfo() - * it perhaps doesn't matter that much since EAI_FAIL, EAI_NONAME and - * EAI_NODATA are all permanent failure codes and the causes are all - * permanent failures in the sense that there is no point in retrying - * later." - * - * So this is what we do, except FreeBSD removed EAI_NODATA some time ago, - * so that's conditional. - */ - if (rv == EAI_NONAME || rv == EAI_FAIL || -#ifdef EAI_NODATA - rv == EAI_NODATA || -#endif - (retries >= 0 && !retries--)) { - logger.error() << "Failed to resolve the address endpoint"; - return false; - } - - logger.warning() << "Trying again in" << (timeout / 1000000.0) << "seconds"; - usleep(timeout); - } - - if ((resolved->ai_family == AF_INET && - resolved->ai_addrlen == sizeof(struct sockaddr_in)) || - (resolved->ai_family == AF_INET6 && - resolved->ai_addrlen == sizeof(struct sockaddr_in6))) { - memcpy(sa, resolved->ai_addr, resolved->ai_addrlen); - return true; - } - - logger.error() << "Invalid endpoint" << address; - return false; -} - -bool WireguardUtilsLinux::addPeerPrefix(wg_peer* peer, - const IPAddressRange& prefix) { - Q_ASSERT(peer); - - wg_allowedip* allowedip = - static_cast(calloc(1, sizeof(*allowedip))); - if (!allowedip) { - logger.error() << "Allocation failure"; - return false; - } - - if (!peer->first_allowedip) { - peer->first_allowedip = allowedip; - } else { - peer->last_allowedip->next_allowedip = allowedip; - } - peer->last_allowedip = allowedip; - - return buildAllowedIp(allowedip, prefix); -} - -static void nlmsg_append_attr(char* buf, size_t maxlen, int attrtype, - const void* attrdata, size_t attrlen) { - struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; - size_t newlen = NLMSG_ALIGN(nlmsg->nlmsg_len) + RTA_SPACE(attrlen); - if (newlen <= maxlen) { - struct rtattr* attr = (struct rtattr*)(buf + NLMSG_ALIGN(nlmsg->nlmsg_len)); - 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(char* buf, size_t maxlen, int attrtype, - uint32_t value) { - nlmsg_append_attr(buf, maxlen, attrtype, &value, sizeof(value)); -} - -bool WireguardUtilsLinux::rtmSendRule(int action, int flags, int addrfamily) { - constexpr size_t fib_max_size = - sizeof(struct fib_rule_hdr) + 2 * RTA_SPACE(sizeof(uint32_t)); - - char buf[NLMSG_SPACE(fib_max_size)]; - struct nlmsghdr* nlmsg = (struct nlmsghdr*)buf; - struct fib_rule_hdr* rule = (struct fib_rule_hdr*)NLMSG_DATA(nlmsg); - struct sockaddr_nl nladdr; - memset(&nladdr, 0, sizeof(nladdr)); - nladdr.nl_family = AF_NETLINK; - - /* Create a routing policy rule to select the wireguard routing table for - * unmarked packets. This is equivalent to: - * ip rule add not fwmark $WG_FIREWALL_MARK table $WG_ROUTE_TABLE - */ - memset(buf, 0, sizeof(buf)); - nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); - nlmsg->nlmsg_type = action; - nlmsg->nlmsg_flags = flags; - nlmsg->nlmsg_pid = getpid(); - nlmsg->nlmsg_seq = m_nlseq++; - rule->family = addrfamily; - rule->table = RT_TABLE_UNSPEC; - rule->action = FR_ACT_TO_TBL; - rule->flags = FIB_RULE_INVERT; - nlmsg_append_attr32(buf, sizeof(buf), FRA_FWMARK, WG_FIREWALL_MARK); - nlmsg_append_attr32(buf, sizeof(buf), FRA_TABLE, WG_ROUTE_TABLE); - ssize_t result = sendto(m_nlsock, buf, nlmsg->nlmsg_len, 0, - (struct sockaddr*)&nladdr, sizeof(nladdr)); - if (result != nlmsg->nlmsg_len) { - return false; - } - - /* Create a routing policy rule to suppress zero-length prefix lookups from - * in the main routing table. This is equivalent to: - * ip rule add table main suppress_prefixlength 0 - */ - memset(buf, 0, sizeof(buf)); - nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); - nlmsg->nlmsg_type = action; - nlmsg->nlmsg_flags = flags; - nlmsg->nlmsg_pid = getpid(); - nlmsg->nlmsg_seq = m_nlseq++; - rule->family = addrfamily; - rule->table = RT_TABLE_MAIN; - rule->action = FR_ACT_TO_TBL; - rule->flags = 0; - nlmsg_append_attr32(buf, sizeof(buf), FRA_SUPPRESS_PREFIXLEN, 0); - result = sendto(m_nlsock, buf, nlmsg->nlmsg_len, 0, (struct sockaddr*)&nladdr, - sizeof(nladdr)); - if (result != nlmsg->nlmsg_len) { - return false; - } - - return true; -} - -void WireguardUtilsLinux::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 = (struct nlmsgerr*)NLMSG_DATA(nlmsg); - if (err->error != 0) { - logger.debug() << "Netlink request failed:" << strerror(-err->error); - } - nlmsg = NLMSG_NEXT(nlmsg, len); - } -} - -// static -bool WireguardUtilsLinux::setupCgroupClass(const QString& path, - unsigned long classid) { - logger.debug() << "Creating control group:" << path; - int flags = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; - int err = mkdir(qPrintable(path), flags); - if ((err < 0) && (errno != EEXIST)) { - logger.error() << "Failed to create" << path + ":" << strerror(errno); - return false; - } - - QString netClassPath = path + "/net_cls.classid"; - FILE* fp = fopen(qPrintable(netClassPath), "w"); - if (!fp) { - logger.error() << "Failed to set classid:" << strerror(errno); - return false; - } - fprintf(fp, "%lu", classid); - fclose(fp); - return true; -} - -QString WireguardUtilsLinux::getExcludeCgroup() const { - if (m_cgroups.isNull()) { - return QString(); - } - return m_cgroups + VPN_EXCLUDE_CGROUP; -} - -QString WireguardUtilsLinux::getBlockCgroup() const { - if (m_cgroups.isNull()) { - return QString(); - } - return m_cgroups + VPN_BLOCK_CGROUP; -} - -// static -bool WireguardUtilsLinux::buildAllowedIp(wg_allowedip* ip, - const IPAddressRange& prefix) { - if (prefix.type() == IPAddressRange::IPv4) { - ip->family = AF_INET; - ip->cidr = prefix.range(); - return inet_pton(AF_INET, qPrintable(prefix.ipAddress()), &ip->ip4) == 1; - } - if (prefix.type() == IPAddressRange::IPv6) { - ip->family = AF_INET6; - ip->cidr = prefix.range(); - return inet_pton(AF_INET6, qPrintable(prefix.ipAddress()), &ip->ip6) == 1; - } - return false; -} - -// static -QString WireguardUtilsLinux::printablePubkey(const QString& pubkey) { - if (pubkey.length() < 12) { - return pubkey; - } else { - return pubkey.left(6) + "..." + pubkey.right(6); - } -} diff --git a/client/platforms/linux/daemon/wireguardutilslinux.h b/client/platforms/linux/daemon/wireguardutilslinux.h deleted file mode 100644 index 9cb22b90..00000000 --- a/client/platforms/linux/daemon/wireguardutilslinux.h +++ /dev/null @@ -1,56 +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 WIREGUARDUTILSLINUX_H -#define WIREGUARDUTILSLINUX_H - -#include "daemon/wireguardutils.h" -#include -#include -#include - -class WireguardUtilsLinux final : public WireguardUtils { - Q_OBJECT - - public: - WireguardUtilsLinux(QObject* parent); - ~WireguardUtilsLinux(); - bool interfaceExists() override; - bool addInterface(const InterfaceConfig& config) override; - bool deleteInterface() override; - - bool updatePeer(const InterfaceConfig& config) override; - bool deletePeer(const QString& pubkey) override; - peerStatus getPeerStatus(const QString& pubkey) override; - - bool updateRoutePrefix(const IPAddressRange& prefix, int hopindex) override; - bool deleteRoutePrefix(const IPAddressRange& prefix, int hopindex) override; - - QString getDefaultCgroup() const { return m_cgroups; } - QString getExcludeCgroup() const; - QString getBlockCgroup() const; - - private: - QStringList currentInterfaces(); - bool setPeerEndpoint(struct sockaddr* sa, const QString& address, int port); - bool addPeerPrefix(struct wg_peer* peer, const IPAddressRange& prefix); - bool rtmSendRule(int action, int flags, int addrfamily); - bool rtmSendRoute(int action, int flags, const IPAddressRange& prefix, - int hopindex); - static bool setupCgroupClass(const QString& path, unsigned long classid); - static bool buildAllowedIp(struct wg_allowedip*, - const IPAddressRange& prefix); - - static QString printablePubkey(const QString& pubkey); - - int m_nlsock = -1; - int m_nlseq = 0; - QSocketNotifier* m_notifier = nullptr; - QString m_cgroups; - - private slots: - void nlsockReady(); -}; - -#endif // WIREGUARDUTILSLINUX_H