diff --git a/libs/models/kcmidentitymodel.cpp b/libs/models/kcmidentitymodel.cpp --- a/libs/models/kcmidentitymodel.cpp +++ b/libs/models/kcmidentitymodel.cpp @@ -90,7 +90,9 @@ return tooltip; } else if (role == KcmVpnConnectionExportable) { if (type == NetworkManager::ConnectionSettings::Vpn && vpnSetting) { - return (vpnSetting->serviceType().endsWith(QLatin1String("vpnc")) || vpnSetting->serviceType().endsWith(QLatin1String("openvpn"))); + return (vpnSetting->serviceType().endsWith(QLatin1String("vpnc")) || + vpnSetting->serviceType().endsWith(QLatin1String("openvpn")) || + vpnSetting->serviceType().endsWith(QLatin1String("wireguard"))); } return false; } else { diff --git a/vpn/CMakeLists.txt b/vpn/CMakeLists.txt --- a/vpn/CMakeLists.txt +++ b/vpn/CMakeLists.txt @@ -11,4 +11,5 @@ add_subdirectory(sstp) add_subdirectory(strongswan) add_subdirectory(vpnc) +add_subdirectory(wireguard) diff --git a/vpn/wireguard/CMakeLists.txt b/vpn/wireguard/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/vpn/wireguard/CMakeLists.txt @@ -0,0 +1,35 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"plasmanetworkmanagement_wireguardui\") + +set(wireguard_SRCS + ../../libs/debug.cpp + wireguard.cpp + wireguardwidget.cpp + wireguardauth.cpp + wireguardadvancedwidget.cpp + wireguardutils.cpp +) + +ki18n_wrap_ui(wireguard_SRCS wireguard.ui wireguardadvanced.ui wireguardauth.ui) + +add_library(plasmanetworkmanagement_wireguardui ${wireguard_SRCS}) + +kcoreaddons_desktop_to_json(plasmanetworkmanagement_wireguardui plasmanetworkmanagement_wireguardui.desktop) + +target_link_libraries(plasmanetworkmanagement_wireguardui + plasmanm_internal + plasmanm_editor + Qt5::Widgets + Qt5::Network + Qt5::DBus + KF5::NetworkManagerQt + KF5::Service + KF5::Completion + KF5::I18n + KF5::WidgetsAddons + KF5::KIOWidgets + KF5::CoreAddons +) + +install(TARGETS plasmanetworkmanagement_wireguardui DESTINATION ${PLUGIN_INSTALL_DIR}) + +install(FILES plasmanetworkmanagement_wireguardui.desktop DESTINATION ${SERVICES_INSTALL_DIR}) diff --git a/vpn/wireguard/nm-wireguard-service.h b/vpn/wireguard/nm-wireguard-service.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/nm-wireguard-service.h @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* nm-openvpn-service - openvpn integration with NetworkManager + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef NM_WIREGUARD_SERVICE_H +#define NM_WIREGUARD_SERVICE_H + +#define NM_DBUS_SERVICE_WIREGUARD "org.freedesktop.NetworkManager.wireguard" +#define NM_DBUS_INTERFACE_WIREGUARD "org.freedesktop.NetworkManager.wireguard" +#define NM_DBUS_PATH_WIREGUARD "/org/freedesktop/NetworkManager/wireguard" + +#define NM_WG_KEY_ADDR_IP4 "local-ip4" +#define NM_WG_KEY_ADDR_IP6 "local-ip6" +#define NM_WG_KEY_LISTEN_PORT "local-listen-port" +#define NM_WG_KEY_PRIVATE_KEY "local-private-key" +#define NM_WG_KEY_DNS "connection-dns" +#define NM_WG_KEY_MTU "connection-mtu" +#define NM_WG_KEY_TABLE "connection_table" +#define NM_WG_KEY_PRE_UP "script-pre-up" +#define NM_WG_KEY_POST_UP "script-post-up" +#define NM_WG_KEY_PRE_DOWN "script-pre-down" +#define NM_WG_KEY_POST_DOWN "script-post-down" +#define NM_WG_KEY_PUBLIC_KEY "peer-public-key" +#define NM_WG_KEY_ALLOWED_IPS "peer-allowed-ips" +#define NM_WG_KEY_ENDPOINT "peer-endpoint" +#define NM_WG_KEY_PRESHARED_KEY "peer-preshared-key" +#define NM_WG_KEY_FWMARK "fwmark" + +#define NM_OPENVPN_KEY_AUTH "auth" +#define NM_OPENVPN_KEY_CA "ca" +#define NM_OPENVPN_KEY_CERT "cert" +#define NM_OPENVPN_KEY_CIPHER "cipher" +#define NM_OPENVPN_KEY_KEYSIZE "keysize" +#define NM_OPENVPN_KEY_COMP_LZO "comp-lzo" +#define NM_OPENVPN_KEY_CONNECTION_TYPE "connection-type" +#define NM_OPENVPN_KEY_FLOAT "float" +#define NM_OPENVPN_KEY_FRAGMENT_SIZE "fragment-size" +#define NM_OPENVPN_KEY_KEY "key" +#define NM_OPENVPN_KEY_LOCAL_IP "local-ip" /* ??? */ +#define NM_OPENVPN_KEY_MSSFIX "mssfix" +#define NM_OPENVPN_KEY_NS_CERT_TYPE "ns-cert-type" +#define NM_OPENVPN_KEY_PING "ping" +#define NM_OPENVPN_KEY_PING_EXIT "ping-exit" +#define NM_OPENVPN_KEY_PING_RESTART "ping-restart" +#define NM_OPENVPN_KEY_PORT "port" +#define NM_OPENVPN_KEY_PROTO_TCP "proto-tcp" +#define NM_OPENVPN_KEY_PROXY_TYPE "proxy-type" +#define NM_OPENVPN_KEY_PROXY_SERVER "proxy-server" +#define NM_OPENVPN_KEY_PROXY_PORT "proxy-port" +#define NM_OPENVPN_KEY_PROXY_RETRY "proxy-retry" +#define NM_OPENVPN_KEY_HTTP_PROXY_USERNAME "http-proxy-username" +#define NM_OPENVPN_KEY_REMOTE "remote" +#define NM_OPENVPN_KEY_REMOTE_RANDOM "remote-random" +#define NM_OPENVPN_KEY_REMOTE_IP "remote-ip" +#define NM_OPENVPN_KEY_STATIC_KEY "static-key" +#define NM_OPENVPN_KEY_STATIC_KEY_DIRECTION "static-key-direction" +#define NM_OPENVPN_KEY_TA "ta" +#define NM_OPENVPN_KEY_TA_DIR "ta-dir" +#define NM_OPENVPN_KEY_TUNNEL_MTU "tunnel-mtu" +#define NM_OPENVPN_KEY_USERNAME "username" +#define NM_OPENVPN_KEY_TAP_DEV "tap-dev" +#define NM_OPENVPN_KEY_DEV "dev" +#define NM_OPENVPN_KEY_DEV_TYPE "dev-type" +#define NM_OPENVPN_KEY_TUN_IPV6 "tun-ipv6" +#define NM_OPENVPN_KEY_TLS_CIPHER "tls-cipher" +#define NM_OPENVPN_KEY_TLS_CRYPT "tls-crypt" +#define NM_OPENVPN_KEY_TLS_REMOTE "tls-remote" +#define NM_OPENVPN_KEY_VERIFY_X509_NAME "verify-x509-name" +#define NM_OPENVPN_KEY_REMOTE_CERT_TLS "remote-cert-tls" +#define NM_OPENVPN_KEY_MAX_ROUTES "max-routes" + +#define NM_OPENVPN_KEY_PASSWORD "password" +#define NM_OPENVPN_KEY_CERTPASS "cert-pass" +#define NM_OPENVPN_KEY_HTTP_PROXY_PASSWORD "http-proxy-password" +/* Internal auth-dialog -> service token indicating that no secrets are + * required for the connection. + */ +#define NM_OPENVPN_KEY_NOSECRET "no-secret" + +#define NM_OPENVPN_KEY_RENEG_SECONDS "reneg-seconds" + +#define NM_OPENVPN_AUTH_NONE "none" +#define NM_OPENVPN_AUTH_RSA_MD4 "RSA-MD4" +#define NM_OPENVPN_AUTH_MD5 "MD5" +#define NM_OPENVPN_AUTH_SHA1 "SHA1" +#define NM_OPENVPN_AUTH_SHA224 "SHA224" +#define NM_OPENVPN_AUTH_SHA256 "SHA256" +#define NM_OPENVPN_AUTH_SHA384 "SHA384" +#define NM_OPENVPN_AUTH_SHA512 "SHA512" +#define NM_OPENVPN_AUTH_RIPEMD160 "RIPEMD160" + +#define NM_OPENVPN_CONTYPE_TLS "tls" +#define NM_OPENVPN_CONTYPE_STATIC_KEY "static-key" +#define NM_OPENVPN_CONTYPE_PASSWORD "password" +#define NM_OPENVPN_CONTYPE_PASSWORD_TLS "password-tls" + +/* arguments of "--remote-cert-tls" */ +#define NM_OPENVPN_REM_CERT_TLS_CLIENT "client" +#define NM_OPENVPN_REM_CERT_TLS_SERVER "server" + +/* arguments of "--ns-cert-type" */ +#define NM_OPENVPN_NS_CERT_TYPE_CLIENT "client" +#define NM_OPENVPN_NS_CERT_TYPE_SERVER "server" + +/* possible types for verify-x509-name */ +#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_SUBJECT "subject" +#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_NAME "name" +#define NM_OPENVPN_VERIFY_X509_NAME_TYPE_NAME_PREFIX "name-prefix" + +#endif /* NM_WIREGUARD_SERVICE_H */ diff --git a/vpn/wireguard/passwordfield.h b/vpn/wireguard/passwordfield.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/passwordfield.h @@ -0,0 +1,71 @@ +/* + Copyright 2015 Jan Grulich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PLASMA_NM_PASSWORD_FIELD_H +#define PLASMA_NM_PASSWORD_FIELD_H + +#include +#include + +#include + +class Q_DECL_EXPORT PasswordField : public QWidget +{ + Q_OBJECT + Q_PROPERTY(bool passwordModeEnabled WRITE setPasswordModeEnabled) +public: + enum PasswordOption { + StoreForUser, + StoreForAllUsers, + AlwaysAsk, + NotRequired + }; + + explicit PasswordField(QWidget *parent = 0, Qt::WindowFlags f = Qt::WindowFlags()); + + void setMaxLength(int maxLength); + void setPasswordModeEnabled(bool passwordMode); + void setPasswordOptionsEnabled(bool enable); + void setPasswordNotRequiredEnabled(bool enable); + + PasswordOption passwordOption() const; + void setPasswordOption(PasswordOption option); + + void setText(const QString &text); + QString text() const; + +private Q_SLOTS: + void changePasswordOption(int index); + void showToggleEchoModeAction(const QString &text); + void toggleEchoMode(); + +Q_SIGNALS: + void textChanged(const QString &text); + void passwordOptionChanged(PasswordOption option); + +private: + PasswordOption m_currentPasswordOption; + QVBoxLayout *m_layout; + QLineEdit *m_passwordField; + QComboBox *m_passwordOptionsMenu; + QAction *m_toggleEchoModeAction; +}; + +#endif // PLASMA_NM_PASSWORD_FIELD_H diff --git a/vpn/wireguard/passwordfield.cpp b/vpn/wireguard/passwordfield.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/passwordfield.cpp @@ -0,0 +1,166 @@ +/* + Copyright 2015 Jan Grulich + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "passwordfield.h" + +#include +#include + +#include +#include +#include + +PasswordField::PasswordField(QWidget *parent, Qt::WindowFlags f) + : QWidget(parent, f) + , m_currentPasswordOption(StoreForUser) +{ + m_layout = new QVBoxLayout(this); + // The widget will be already in layout, thus reset content margins + // to align it with the rest of widgets + m_layout->setContentsMargins(0, 0, 0, 0); + + m_passwordField = new QLineEdit(this); + connect(m_passwordField, &QLineEdit::textChanged, this, &PasswordField::textChanged); + + if (KAuthorized::authorize(QStringLiteral("lineedit_reveal_password"))) { + m_toggleEchoModeAction = m_passwordField->addAction(QIcon::fromTheme(QStringLiteral("visibility")), QLineEdit::TrailingPosition); + m_toggleEchoModeAction->setVisible(false); + m_toggleEchoModeAction->setToolTip(i18n("Change the visibility of the password")); + connect(m_passwordField, &QLineEdit::textChanged, this, &PasswordField::showToggleEchoModeAction); + connect(m_toggleEchoModeAction, &QAction::triggered, this, &PasswordField::toggleEchoMode); + } + + m_layout->addWidget(m_passwordField); + + m_passwordOptionsMenu = new QComboBox(this); + m_passwordOptionsMenu->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); + + m_passwordOptionsMenu->addItem(QIcon::fromTheme(QStringLiteral("document-save")), i18n("Store password for this user only (encrypted)"), StoreForUser); + m_passwordOptionsMenu->addItem(QIcon::fromTheme(QStringLiteral("document-save-all")), i18n("Store password for all users (not encrypted)"), StoreForAllUsers); + m_passwordOptionsMenu->addItem(QIcon::fromTheme(QStringLiteral("dialog-messages")), i18n("Ask for this password every time"), AlwaysAsk); + // Do not add by default + // m_passwordOptionsMenu->addItem(QIcon::fromTheme(QStringLiteral("document-close")), i18n("This password is not required"), NotRequired); + + if (KWallet::Wallet::isEnabled()) { + m_passwordOptionsMenu->setCurrentIndex(0); + } else { + m_passwordOptionsMenu->setCurrentIndex(1); + m_currentPasswordOption = StoreForAllUsers; + } + + connect(m_passwordOptionsMenu, static_cast(&QComboBox::currentIndexChanged), this, &PasswordField::changePasswordOption); + + // Disable by default + m_passwordOptionsMenu->setVisible(false); + // m_layout->addWidget(m_passwordOptionsMenu); + + setLayout(m_layout); +} + +void PasswordField::setMaxLength(int maxLength) +{ + m_passwordField->setMaxLength(maxLength); +} + +void PasswordField::setPasswordModeEnabled(bool enable) +{ + m_passwordField->setEchoMode(enable ? QLineEdit::Password : QLineEdit::Normal); +} + +void PasswordField::setPasswordOptionsEnabled(bool enable) +{ + if (enable) { + m_layout->addWidget(m_passwordOptionsMenu); + m_passwordOptionsMenu->setVisible(true); + } else { + m_layout->removeWidget(m_passwordOptionsMenu); + m_passwordOptionsMenu->setVisible(false); + } +} + +void PasswordField::setPasswordNotRequiredEnabled(bool enable) +{ + if (enable) { + const int index = m_passwordOptionsMenu->findData(NotRequired); + if (index == -1) { + m_passwordOptionsMenu->addItem(QIcon::fromTheme(QStringLiteral("document-close")), i18n("This password is not required"), NotRequired); + } + } else { + const int index = m_passwordOptionsMenu->findData(NotRequired); + if (index != -1) { + m_passwordOptionsMenu->removeItem(index); + } + } +} + +void PasswordField::setText(const QString &text) +{ + m_passwordField->setText(text); +} + +QString PasswordField::text() const +{ + return m_passwordField->text(); +} + +PasswordField::PasswordOption PasswordField::passwordOption() const +{ + return m_currentPasswordOption; +} + +void PasswordField::setPasswordOption(PasswordField::PasswordOption option) +{ + const int index = m_passwordOptionsMenu->findData(option); + if (index != -1) { + m_passwordOptionsMenu->setCurrentIndex(index); + } +} + +void PasswordField::showToggleEchoModeAction(const QString &text) +{ + m_toggleEchoModeAction->setVisible(!text.isEmpty()); +} + +void PasswordField::toggleEchoMode() +{ + if (m_passwordField->echoMode() == QLineEdit::Password) { + m_passwordField->setEchoMode(QLineEdit::Normal); + m_toggleEchoModeAction->setIcon(QIcon::fromTheme(QStringLiteral("hint"))); + } else if (m_passwordField->echoMode() == QLineEdit::Normal) { + m_passwordField->setEchoMode(QLineEdit::Password); + m_toggleEchoModeAction->setIcon(QIcon::fromTheme(QStringLiteral("visibility"))); + } +} + +void PasswordField::changePasswordOption(int index) +{ + Q_UNUSED(index); + + m_currentPasswordOption = (PasswordOption)m_passwordOptionsMenu->currentData().toUInt(); + + if (m_currentPasswordOption == PasswordField::NotRequired || m_currentPasswordOption == PasswordField::AlwaysAsk) { + m_passwordField->clear(); + m_passwordField->setDisabled(true); + } else { + m_passwordField->setEnabled(true); + } + + Q_EMIT passwordOptionChanged(m_currentPasswordOption); +} diff --git a/vpn/wireguard/plasmanetworkmanagement_wireguardui.desktop b/vpn/wireguard/plasmanetworkmanagement_wireguardui.desktop new file mode 100644 --- /dev/null +++ b/vpn/wireguard/plasmanetworkmanagement_wireguardui.desktop @@ -0,0 +1,99 @@ +[Desktop Entry] +Type=Service +Icon= +ServiceTypes=PlasmaNetworkManagement/VpnUiPlugin +X-KDE-Library=plasmanetworkmanagement_wireguardui +X-NetworkManager-Services=org.freedesktop.NetworkManager.wireguard +X-KDE-PluginInfo-Author=Lukáš Tinkl +X-KDE-PluginInfo-Email=lukas@kde.org +X-KDE-PluginInfo-Name=plasmanetworkmanagement_wireguardui +X-KDE-PluginInfo-Version=0.1 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category=VPNService +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=false +Name=Wireguard +Name[ar]=Wireguard +Name[bg]=Wireguard +Name[bs]=Wireguard +Name[ca]=Wireguard +Name[ca@valencia]=Wireguard +Name[cs]=Wireguard +Name[da]=Wireguard +Name[de]=Wireguard +Name[el]=Wireguard +Name[en_GB]=Wireguard +Name[es]=Wireguard +Name[et]=Wireguard +Name[eu]=Wireguard +Name[fi]=Wireguard +Name[fr]=Wireguard +Name[gl]=Wireguard +Name[he]=Wireguard +Name[hu]=Wireguard +Name[ia]=Wireguard +Name[it]=Wireguard +Name[ko]=Wireguard +Name[lt]=Wireguard +Name[nb]=Wireguard +Name[nds]=Wireguard +Name[nl]=Wireguard +Name[nn]=Wireguard +Name[pa]=Wireguard +Name[pl]=Wireguard +Name[pt]=Wireguard +Name[pt_BR]=Wireguard +Name[ro]=Wireguard +Name[ru]=Wireguard +Name[sk]=Wireguard +Name[sl]=Wireguard +Name[sr]=ОпенВПН +Name[sr@ijekavianlatin]=Wireguard +Name[sr@latin]=Wireguard +Name[sv]=Wireguard +Name[tr]=Wireguard +Name[ug]=Wireguard +Name[uk]=Wireguard +Name[x-test]=xxWireguardxx +Name[zh_CN]=Wireguard +Name[zh_TW]=Wireguard +Comment=Compatible with the Wireguard server +Comment[ar]=متوافق مع خادوم Wireguard +Comment[ca]=Compatible amb el servidor Wireguard +Comment[ca@valencia]=Compatible amb el servidor Wireguard +Comment[da]=Kompatibel med Wireguard-serveren +Comment[de]=Mit dem Wireguard-Server kompatibel +Comment[el]=Συμβατό με τον διακομιστή Wireguard +Comment[en_GB]=Compatible with the Wireguard server +Comment[es]=Compatible con el servidor de Wireguard +Comment[et]=Ühildub Wireguard-serveritega +Comment[eu]=Wireguard zerbitzariarekin bateragarria +Comment[fi]=Yhteensopiva Wireguard-palvelimen kanssa +Comment[fr]=Compatible avec le serveur Wireguard +Comment[gl]=Compatíbel con servidores Wireguard. +Comment[he]=תואימות עם שרתי Wireguard +Comment[hu]=Kompatibilis az Wireguard kiszolgálóval +Comment[it]=Compatibile con il server Wireguard +Comment[ko]=Wireguard 서버와 호환됨 +Comment[lt]=Suderinamas su Wireguard serveriu +Comment[nl]=Compatibel met de Wireguard-server +Comment[nn]=Kompatibel med Wireguard-tenaren +Comment[pa]=Wireguard ਸਰਵਰ ਲਈ ਢੁਕਵਾਂ +Comment[pl]=Zgodny z serwerem Wireguard +Comment[pt]=Compatível com o servidor Wireguard +Comment[pt_BR]=Compatível com o servidor Wireguard +Comment[ru]=Совместимая с сервером Wireguard +Comment[sk]=Kompatibilné s Wireguard serverom +Comment[sl]=Združljiv s strežnikom Wireguard +Comment[sr]=Сагласно са сервером ОпенВПН‑а +Comment[sr@ijekavian]=Сагласно са сервером ОпенВПН‑а +Comment[sr@ijekavianlatin]=Saglasno sa serverom Wireguard‑a +Comment[sr@latin]=Saglasno sa serverom Wireguard‑a +Comment[sv]=Kompatibel med Wireguard-server +Comment[tr]=Wireguard sunucusuyla uyumlu +Comment[uk]=Сумісний із сервером Wireguard +Comment[x-test]=xxCompatible with the Wireguard serverxx +Comment[zh_CN]=兼容 Wireguard 服务器 +Comment[zh_TW]=與 Wireguard 伺服器相容 + diff --git a/vpn/wireguard/wireguard.h b/vpn/wireguard/wireguard.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguard.h @@ -0,0 +1,43 @@ +/* + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef PLASMANM_WIREGUARD_H +#define PLASMANM_WIREGUARD_H + +#include "vpnuiplugin.h" + +#include +#include + +class Q_DECL_EXPORT WireGuardUiPlugin : public VpnUiPlugin +{ +Q_OBJECT +public: + explicit WireGuardUiPlugin(QObject * parent = 0, const QVariantList& = QVariantList()); + virtual ~WireGuardUiPlugin(); + SettingWidget * widget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent = 0); + SettingWidget * askUser(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent = 0); + + QString suggestedFileName(const NetworkManager::ConnectionSettings::Ptr &connection) const; + QString supportedFileExtensions() const; + NMVariantMapMap importConnectionSettings(const QString &fileName); + bool exportConnectionSettings(const NetworkManager::ConnectionSettings::Ptr &connection, const QString &fileName); +}; + +#endif // PLASMANM_WIREGUARD_H diff --git a/vpn/wireguard/wireguard.cpp b/vpn/wireguard/wireguard.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguard.cpp @@ -0,0 +1,476 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "wireguard.h" +#include "wireguardutils.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "wireguardwidget.h" +#include "wireguardauth.h" + +#include + +#include "nm-wireguard-service.h" + +K_PLUGIN_FACTORY_WITH_JSON(WireGuardUiPluginFactory, "plasmanetworkmanagement_wireguardui.json", registerPlugin();) + +#define NMV_WG_TAG_INTERFACE "[Interface]" +#define NMV_WG_TAG_PRIVATE_KEY "PrivateKey" +#define NMV_WG_TAG_LISTEN_PORT "ListenPort" +#define NMV_WG_TAG_ADDRESS "Address" +#define NMV_WG_TAG_DNS "DNS" +#define NMV_WG_TAG_MTU "MTU" +#define NMV_WG_TAG_TABLE "Table" +#define NMV_WG_TAG_PRE_UP "PreUp" +#define NMV_WG_TAG_POST_UP "PostUp" +#define NMV_WG_TAG_PRE_DOWN "PreDown" +#define NMV_WG_TAG_POST_DOWN "PostDown" +#define NMV_WG_TAG_FWMARK "FwMark" +#define NMV_WG_ASSIGN "=" + +#define NMV_WG_TAG_PEER "[Peer]" +#define NMV_WG_TAG_PUBLIC_KEY "PublicKey" +#define NMV_WG_TAG_ALLOWED_IPS "AllowedIPs" +#define NMV_WG_TAG_ENDPOINT "Endpoint" +#define NMV_WG_TAG_PRESHARED_KEY "PresharedKey" + + +WireGuardUiPlugin::WireGuardUiPlugin(QObject * parent, const QVariantList &) : VpnUiPlugin(parent) +{ +} + +WireGuardUiPlugin::~WireGuardUiPlugin() +{ +} + +SettingWidget * WireGuardUiPlugin::widget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) +{ + WireGuardSettingWidget * wid = new WireGuardSettingWidget(setting, parent); + return wid; +} + +SettingWidget * WireGuardUiPlugin::askUser(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) +{ + return new WireGuardAuthWidget(setting, parent); +} + +QString WireGuardUiPlugin::suggestedFileName(const NetworkManager::ConnectionSettings::Ptr &connection) const +{ + return connection->id() + "_wireguard.conf"; +} + +QString WireGuardUiPlugin::supportedFileExtensions() const +{ + return "*.conf"; +} + +NMVariantMapMap WireGuardUiPlugin::importConnectionSettings(const QString &fileName) +{ + NMVariantMapMap result; + + QFile impFile(fileName); + if (!impFile.open(QFile::ReadOnly|QFile::Text)) { + mError = VpnUiPlugin::Error; + mErrorMessage = i18n("Could not open file"); + return result; + } + + const QString connectionName = QFileInfo(fileName).completeBaseName(); + NMStringMap dataMap; + QVariantMap ipv4Data; + + QString proxy_type; + QString proxy_user; + QString proxy_passwd; + bool have_address = false; + bool have_private_key = false; + bool have_public_key = false; + bool have_allowed_ips = false; + bool have_endpoint = false; + + QTextStream in(&impFile); + enum {IDLE, INTERFACE_SECTION, PEER_SECTION} current_state = IDLE; + + while (!in.atEnd()) { + QStringList key_value; + QString line = in.readLine(); + + // Ignore blank lines + if (line.isEmpty()) { + continue; + } + key_value.clear(); + key_value << line.split(QRegExp("\\s+=\\s*")); // Split on the ' = ' + + if (key_value[0] == NMV_WG_TAG_INTERFACE) + { + if (current_state == IDLE) + { + current_state = INTERFACE_SECTION; + continue; + } + else + { + // BAA - ERROR + break; + } + } + + else if (key_value[0] == NMV_WG_TAG_PEER) + { + // Currently only on PEER section is allowed + if (current_state == INTERFACE_SECTION) + { + current_state = PEER_SECTION; + continue; + } + else + { + // BAA - ERROR + break; + } + } + + // If we didn't get an '=' sign in the line, it's probably an error but + // we're going to treat it as a comment and ignore it + if (key_value.length() < 2) + continue; + + // If we are in the [Interface] section look for the possible tags + if (current_state == INTERFACE_SECTION) + { + // Address + if (key_value[0] == NMV_WG_TAG_ADDRESS) + { + QStringList address_list; + address_list << key_value[1].split(QRegExp("\\s*,\\s*")); + for (int i = 0;i < address_list.size(); i++) + { + if (WireGuardUtils::is_ip4(address_list[i])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_ADDR_IP4), address_list[i]); + have_address = true; + } + else if (WireGuardUtils::is_ip6(address_list[i])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_ADDR_IP6), address_list[i]); + have_address = true; + } + } + } + + // Listen Port + else if (key_value[0] == NMV_WG_TAG_LISTEN_PORT) + { + if (WireGuardUtils::is_num_valid(key_value[1], 0, 65535)) + { + dataMap.insert(QLatin1String(NM_WG_KEY_LISTEN_PORT), key_value[1]); + } + } + // Private Key + else if (key_value[0] == NMV_WG_TAG_PRIVATE_KEY) + { + if (WireGuardUtils::is_key_valid(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_PRIVATE_KEY), key_value[1]); + have_private_key = true; + } + } + // DNS + else if (key_value[0] == NMV_WG_TAG_DNS) + { + if (WireGuardUtils::is_ip4(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_DNS), key_value[1]); + } + } + // MTU + else if (key_value[0] == NMV_WG_TAG_MTU) + { + if (WireGuardUtils::is_num_valid(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_MTU), key_value[1]); + } + } + // Table + else if (key_value[0] == NMV_WG_TAG_TABLE) + { + if (WireGuardUtils::is_num_valid(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_TABLE), key_value[1]); + } + } + else if (key_value[0] == NMV_WG_TAG_PRE_UP) + { + dataMap.insert(QLatin1String(NM_WG_KEY_PRE_UP), key_value[1]); + } + else if (key_value[0] == NMV_WG_TAG_POST_UP) + { + dataMap.insert(QLatin1String(NM_WG_KEY_POST_UP), key_value[1]); + } + else if (key_value[0] == NMV_WG_TAG_PRE_DOWN) + { + dataMap.insert(QLatin1String(NM_WG_KEY_PRE_DOWN), key_value[1]); + } + else if (key_value[0] == NMV_WG_TAG_POST_DOWN) + { + dataMap.insert(QLatin1String(NM_WG_KEY_POST_DOWN), key_value[1]); + } + else + { + // We got a wrong field in the Interface section so it + // is an error + mError = VpnUiPlugin::Error; + mErrorMessage = i18n("Invalid key %1 in [Interface] section", key_value[0]); + break; + } + } + else if (current_state == PEER_SECTION) + { + // Public Key + if (key_value[0] == NMV_WG_TAG_PUBLIC_KEY) + { + if (WireGuardUtils::is_key_valid(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_PUBLIC_KEY), key_value[1]); + have_public_key = true; + } + } + // Allowed IPs + else if (key_value[0] == NMV_WG_TAG_ALLOWED_IPS) + { + if (key_value[1].length() > 0) + { + dataMap.insert(QLatin1String(NM_WG_KEY_ALLOWED_IPS), key_value[1]); + have_allowed_ips = true; + } + } + // Endpoint + else if (key_value[0] == NMV_WG_TAG_ENDPOINT) + { + if (key_value[1].length() > 0) + { + dataMap.insert(QLatin1String(NM_WG_KEY_ENDPOINT), key_value[1]); + have_endpoint = true; + } + } + // Preshared Key + else if (key_value[0] == NMV_WG_TAG_PRESHARED_KEY) + { + if (WireGuardUtils::is_key_valid(key_value[1])) + { + dataMap.insert(QLatin1String(NM_WG_KEY_PRESHARED_KEY), key_value[1]); + } + } + } + else // We're in IDLE or unknown state so it's an error + { + // TODO - add error handling + mError = VpnUiPlugin::Error; + mErrorMessage = i18n("Unknown state in import file"); + break; + } + } + if (!have_address || !have_private_key || !have_public_key || !have_endpoint || !have_allowed_ips) + { + + mError = VpnUiPlugin::Error; + mErrorMessage = i18n("File %1 is not a valid WireGuard configuration.", fileName); + return result; + } + + NetworkManager::VpnSetting setting; + setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_WIREGUARD)); + setting.setData(dataMap); + + QVariantMap conn; + conn.insert("id", connectionName); + conn.insert("type", "vpn"); + result.insert("connection", conn); + result.insert("vpn", setting.toMap()); + + impFile.close(); + return result; +} + +bool WireGuardUiPlugin::exportConnectionSettings(const NetworkManager::ConnectionSettings::Ptr &connection, const QString &fileName) +{ + QFile expFile(fileName); + if (! expFile.open(QIODevice::WriteOnly | QIODevice::Text) ) { + mError = VpnUiPlugin::Error; + mErrorMessage = i18n("Could not open file for writing"); + return false; + } + + NMStringMap dataMap; + + NetworkManager::VpnSetting::Ptr vpnSetting = connection->setting(NetworkManager::Setting::Vpn).dynamicCast(); + dataMap = vpnSetting->data(); + + QString line; + + line = QString(NMV_WG_TAG_INTERFACE) + '\n'; + expFile.write(line.toLatin1()); + + // Handle IPv4 and IPv6 addresses. if neither is present it is an error + line = QString("%1 = ").arg(NMV_WG_TAG_ADDRESS); + + if (dataMap.contains(QLatin1String(NM_WG_KEY_ADDR_IP4))) + { + line += dataMap[NM_WG_KEY_ADDR_IP4]; + if (dataMap.contains(QLatin1String(NM_WG_KEY_ADDR_IP6))) + { + line += "," + dataMap[NM_WG_KEY_ADDR_IP6]; + } + } + else if (dataMap.contains(QLatin1String(NM_WG_KEY_ADDR_IP4))) + { + line += dataMap[NM_WG_KEY_ADDR_IP4]; + } + else + { + return false; + } + line += "\n"; + expFile.write(line.toLatin1()); + + // Do Private Key + if (dataMap.contains(QLatin1String(NM_WG_KEY_PRIVATE_KEY))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_PRIVATE_KEY, dataMap[NM_WG_KEY_PRIVATE_KEY]); + } + else + { + return false; + } + expFile.write(line.toLatin1()); + + // Do DNS (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_DNS))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_DNS, dataMap[NM_WG_KEY_DNS]); + expFile.write(line.toLatin1()); + } + + // Do MTU (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_MTU))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_MTU, dataMap[NM_WG_KEY_MTU]); + expFile.write(line.toLatin1()); + } + + // Do Table number (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_TABLE))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_TABLE, dataMap[NM_WG_KEY_TABLE]); + expFile.write(line.toLatin1()); + } + + // Do Listen Port (Not required) + if (dataMap.contains(QLatin1String(NM_WG_KEY_LISTEN_PORT))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_LISTEN_PORT, dataMap[NM_WG_KEY_LISTEN_PORT]); + expFile.write(line.toLatin1()); + } + + // Do FwMark (Not required) + if (dataMap.contains(QLatin1String(NM_WG_KEY_FWMARK))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_FWMARK, dataMap[NM_WG_KEY_FWMARK]); + expFile.write(line.toLatin1()); + } + + // Do the Pre, Post, Up, Down scripte (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_PRE_UP))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_PRE_UP, dataMap[NM_WG_KEY_PRE_UP]); + expFile.write(line.toLatin1()); + } + if (dataMap.contains(QLatin1String(NM_WG_KEY_POST_UP))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_POST_UP, dataMap[NM_WG_KEY_POST_UP]); + expFile.write(line.toLatin1()); + } + if (dataMap.contains(QLatin1String(NM_WG_KEY_PRE_DOWN))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_PRE_DOWN, dataMap[NM_WG_KEY_PRE_DOWN]); + expFile.write(line.toLatin1()); + } + if (dataMap.contains(QLatin1String(NM_WG_KEY_POST_DOWN))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_POST_DOWN, dataMap[NM_WG_KEY_POST_DOWN]); + expFile.write(line.toLatin1()); + } + + // Throw in the "Peer" section header + line = "\n" + QString(NMV_WG_TAG_PEER) + '\n'; + expFile.write(line.toLatin1()); + + // Do Pupblic key (required) + if (dataMap.contains(QLatin1String(NM_WG_KEY_PUBLIC_KEY))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_PUBLIC_KEY, dataMap[NM_WG_KEY_PUBLIC_KEY]); + } + else + { + return false; + } + expFile.write(line.toLatin1()); + + // Do Allowed IP list (Required) + if (dataMap.contains(QLatin1String(NM_WG_KEY_ALLOWED_IPS))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_ALLOWED_IPS, dataMap[NM_WG_KEY_ALLOWED_IPS]); + } + else + { + return false; + } + expFile.write(line.toLatin1()); + + + // Do Endpoint (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_ENDPOINT))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_ENDPOINT, dataMap[NM_WG_KEY_ENDPOINT]); + expFile.write(line.toLatin1()); + } + + // Do Preshared Key (Not required so no error if not present) + if (dataMap.contains(QLatin1String(NM_WG_KEY_PRESHARED_KEY))) + { + line = QString("%1 = %2\n").arg(NMV_WG_TAG_PRESHARED_KEY, dataMap[NM_WG_KEY_PRESHARED_KEY]); + expFile.write(line.toLatin1()); + } + + expFile.close(); + return true; +} + +#include "wireguard.moc" diff --git a/vpn/wireguard/wireguard.ui b/vpn/wireguard/wireguard.ui new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguard.ui @@ -0,0 +1,202 @@ + + + WireGuardProp + + + + 0 + 0 + 468 + 449 + + + + VPN (wireguard) + + + + + 10 + 10 + 91 + 23 + + + + + 16 + 75 + true + + + + Interface + + + + + + 10 + 40 + 451 + 181 + + + + + + + Address (IPv4): + + + + + + + <html><head/><body><p>IPv4 intrnrt address assigned to the local interface. IPv4 or IPv6 address (or both) required</p></body></html> + + + + + + + Address (IPv6): + + + + + + + <html><head/><body><p>IPv6 intrnrt address assigned to the local interface. IPv4 or IPv6 address (or both) required</p></body></html> + + + + + + + Private Key: + + + + + + + <html><head/><body><p>A base64 private key generated by wg genkey. Required.</p></body></html> + + + + + + + DNS: + + + + + + + <html><head/><body><p>IPv4 or IPv6 address to set and the interface's DNS server. Optional.</p></body></html> + + + + + + + + + 10 + 220 + 65 + 23 + + + + + 16 + 75 + true + + + + Peer + + + + + + 10 + 250 + 451 + 131 + + + + + + + Public Key: + + + + + + + <html><head/><body><p>A base64 public key calculated + by wg pubkey from a private key, and usually transmitted out of band to the author of the configuration file. Required.</p></body></html> + + + + + + + Allowed IPs + + + + + + + <html><head/><body><p>A comma-separated list of IP (v4 or v6) addresses with CIDR masks from which incoming traffic for this peer is allowed and to which outgoing traffic for this peer is directed. The catch-all 0.0.0.0/0 may be specified for matching all IPv4 addresses, and ::/0 may be specified for matching all IPv6 addresses.</p></body></html> + + + + + + + Endpoint: + + + + + + + <html><head/><body><p>An endpoint IP or hostname, followed by a colon, and then a port number. Optional.</p></body></html> + + + + + + + + + 310 + 390 + 96 + 39 + + + + Advanced.. + + + + + + PasswordField + QLineEdit +
passwordfield.h
+
+
+ + +
diff --git a/vpn/wireguard/wireguard_global.h b/vpn/wireguard/wireguard_global.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguard_global.h @@ -0,0 +1,12 @@ +#ifndef WIREGUARD_GLOBAL_H +#define WIREGUARD_GLOBAL_H + +#include + +#if defined(WIREGUARD_LIBRARY) +# define WIREGUARDSHARED_EXPORT Q_DECL_EXPORT +#else +# define WIREGUARDSHARED_EXPORT Q_DECL_IMPORT +#endif + +#endif // WIREGUARD_GLOBAL_H diff --git a/vpn/wireguard/wireguardadvanced.ui b/vpn/wireguard/wireguardadvanced.ui new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardadvanced.ui @@ -0,0 +1,203 @@ + + + WireGuardAdvancedWidget + + + + 0 + 0 + 365 + 505 + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + + 16777215 + 25 + + + + + 16 + 75 + true + + + + Intreface + + + + + + + + + Listen Port + + + + + + + <html><head/><body><p>A 16-bit port for listening. Optional; if not specified, chosen randomly.</p></body></html> + + + + + + + MTU + + + + + + + <html><head/><body><p>If not specified, the MTU is automatically determined from the endpoint addresses or the system default route, which is usually a sane choice. However, to manually specify an MTU to override this automatic discovery, this value may be specified explicitly.</p></body></html> + + + + + + + Table + + + + + + + <html><head/><body><p>Controls the routing table to which routes are added. There are two special values: `off' disables the creation of routes altogether, and `auto' (the default) adds routes to the default table and enables special handling of default routes</p></body></html> + + + + + + + FwMark + + + + + + + <html><head/><body><p>A 32-bit fwmark for outgoing packets. If set to 0 or &quot;off&quot;, this option is disabled. May be specified in hexadecimal by prepending &quot;0x&quot;. Optional.</p></body></html> + + + + + + + PreUp Script + + + + + + + <html><head/><body><p>Script snippet which will be executed by bash(1) before setting up the interface, most commonly used to configure custom DNS options or firewall rules. The special string `%i' is expanded to INTERFACE.</p><p>This is NOT the same as the NetworkManager PreUp script which is preferred.</p></body></html> + + + + + + + PostUp Script + + + + + + + <html><head/><body><p>Script snippet which will be executed by bash(1) after setting up the interface, most commonly used to configure custom DNS options or firewall rules. The special string `%i' is expanded to INTERFACE.</p><p>This is NOT the same as the NetworkManager PostUp script which is preferred.</p></body></html> + + + + + + + PreDown Script + + + + + + + <html><head/><body><p>Script snippet which will be executed by bash(1) before tearing down the interface, most commonly used to configure custom DNS options or firewall rules. The special string `%i' is expanded to INTERFACE.</p><p>This is NOT the same as the NetworkManager PreDown script which is preferred.</p></body></html> + + + + + + + PostDown Script + + + + + + + <html><head/><body><p>Script snippet which will be executed by bash(1) after tearing down the interface, most commonly used to configure custom DNS options or firewall rules. The special string `%i' is expanded to INTERFACE.</p><p>This is NOT the same as the NetworkManager PostDown script which is preferred.</p></body></html> + + + + + + + + + + 16777215 + 25 + + + + + 16 + 75 + true + + + + Peer + + + + + + + + + + + Preshared Key + + + + + + + <html><head/><body><p>A base64 preshared key generated by wg genpsk. Optional. </p><p>This option adds an additional layer of symmetric-key cryptography to be mixed into the already existing public-key cryptography, for post-quantum resistance.</p></body></html>o + + + + + + + + + + diff --git a/vpn/wireguard/wireguardadvancedwidget.h b/vpn/wireguard/wireguardadvancedwidget.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardadvancedwidget.h @@ -0,0 +1,69 @@ +/* + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef PLASMA_NM_OPENVPN_ADVANCED_WIDGET_H +#define PLASMA_NM_OPENVPN_ADVANCED_WIDGET_H + +#include "passwordfield.h" + +#include +#include + +#include + +namespace Ui +{ +class WireGuardAdvancedWidget; +} + +class QLineEdit; +class WireGuardAdvancedWidget : public QDialog +{ + Q_OBJECT + + enum CertCheckType { + DontVerify = 0, + VerifyWholeSubjectExactly, + VerifyNameExactly, + VerifyNameByPrefix, + VerifySubjectPartially + }; + +public: + explicit WireGuardAdvancedWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = 0); + ~WireGuardAdvancedWidget(); + + NetworkManager::VpnSetting::Ptr setting() const; + +private Q_SLOTS: + +private: + void loadConfig(); + bool isListenPortValid() const; + bool isMTUValid() const; + bool isTableValid() const; + bool isFwMarkValid() const; + bool isPresharedKeyValid() const; + void setOrClear(NMStringMap &data, QLatin1String key, QString value) const; + Ui::WireGuardAdvancedWidget *m_ui; + class Private; + Private *const d; +}; + +#endif // PLASMA_NM_OPENVPN_ADVANCED_WIDGET_H diff --git a/vpn/wireguard/wireguardadvancedwidget.cpp b/vpn/wireguard/wireguardadvancedwidget.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardadvancedwidget.cpp @@ -0,0 +1,181 @@ +/* + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "wireguardadvancedwidget.h" +#include "ui_wireguardadvanced.h" +#include "nm-wireguard-service.h" +#include "settingwidget.h" +#include "wireguardutils.h" + +#include +#include +#include + +#include +#include +#include + +class WireGuardAdvancedWidget::Private { +public: + NetworkManager::VpnSetting::Ptr setting; +}; + +WireGuardAdvancedWidget::WireGuardAdvancedWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::WireGuardAdvancedWidget) + , d(new Private) +{ + m_ui->setupUi(this); + + setWindowTitle(i18nc("@title: window advanced wireguard properties", "Advanced WireGuard properties")); + + d->setting = setting; + + connect(m_ui->listenPortLineEdit, &QLineEdit::textChanged, this, &WireGuardAdvancedWidget::isListenPortValid); + connect(m_ui->mTULineEdit, &QLineEdit::textChanged, this, &WireGuardAdvancedWidget::isMTUValid); + connect(m_ui->tableLineEdit, &QLineEdit::textChanged, this, &WireGuardAdvancedWidget::isTableValid); + connect(m_ui->fwMarkLineEdit, &QLineEdit::textChanged, this, &WireGuardAdvancedWidget::isFwMarkValid); + connect(m_ui->presharedKeyLineEdit, &PasswordField::textChanged, this, &WireGuardAdvancedWidget::isPresharedKeyValid); + + connect(m_ui->buttonBox, &QDialogButtonBox::accepted, this, &WireGuardAdvancedWidget::accept); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, this, &WireGuardAdvancedWidget::reject); + + m_ui->presharedKeyLineEdit->setPasswordModeEnabled(true); + + KAcceleratorManager::manage(this); + + if (d->setting) { + loadConfig(); + } +} + +WireGuardAdvancedWidget::~WireGuardAdvancedWidget() +{ + delete d; +} + +bool WireGuardAdvancedWidget::isListenPortValid() const +{ + bool valid = WireGuardUtils::is_num_valid(m_ui->listenPortLineEdit->displayText(), 0,65535); + bool present = (0 != m_ui->listenPortLineEdit->displayText().length()); + bool result = valid || !present; + + if (!result) + { + m_ui->listenPortLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + m_ui->listenPortLineEdit->setStyleSheet("* { background-color: }"); + } + + return result; +} + +bool WireGuardAdvancedWidget::isMTUValid() const +{ + bool valid = WireGuardUtils::is_num_valid(m_ui->mTULineEdit->displayText()); + bool present = (0 != m_ui->mTULineEdit->displayText().length()); + bool result = valid || !present; + + if (!result) + { + m_ui->mTULineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + m_ui->mTULineEdit->setStyleSheet("* { background-color: }"); + } + + return result; +} + +bool WireGuardAdvancedWidget::isTableValid() const +{ + return true; +} + +bool WireGuardAdvancedWidget::isFwMarkValid() const +{ + return true; +} + +bool WireGuardAdvancedWidget::isPresharedKeyValid() const +{ + // The preshared key is not required so it is valid if not present + bool valid = (0 == m_ui->presharedKeyLineEdit->text().length() || + WireGuardUtils::is_key_valid(m_ui->presharedKeyLineEdit->text())); + + if (!valid) + { + m_ui->presharedKeyLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + m_ui->presharedKeyLineEdit->setStyleSheet("* { background-color: }"); + } + return valid; +} + +void WireGuardAdvancedWidget::loadConfig() +{ + const NMStringMap dataMap = d->setting->data(); + + m_ui->listenPortLineEdit->setText(dataMap[NM_WG_KEY_LISTEN_PORT]); + m_ui->mTULineEdit->setText(dataMap[NM_WG_KEY_MTU]); + m_ui->tableLineEdit->setText(dataMap[NM_WG_KEY_TABLE]); + m_ui->fwMarkLineEdit->setText(dataMap[NM_WG_KEY_FWMARK]); + m_ui->presharedKeyLineEdit->setText(dataMap[NM_WG_KEY_PRESHARED_KEY]); + m_ui->preUpScriptLineEdit->setText(dataMap[NM_WG_KEY_PRE_UP]); + m_ui->postUpScriptLineEdit->setText(dataMap[NM_WG_KEY_POST_UP]); + m_ui->preDownScriptLineEdit->setText(dataMap[NM_WG_KEY_PRE_DOWN]); + m_ui->postDownScriptLineEdit->setText(dataMap[NM_WG_KEY_POST_DOWN]); + +} + +void WireGuardAdvancedWidget::setOrClear(NMStringMap &data, QLatin1String key, QString value) const +{ + if (0 != value.length()) + { + data.insert(key, value); + } + else + { + data.remove(key); + } +} + +NetworkManager::VpnSetting::Ptr WireGuardAdvancedWidget::setting() const +{ + NMStringMap data; + + setOrClear(data, QLatin1String(NM_WG_KEY_LISTEN_PORT), m_ui->listenPortLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_MTU), m_ui->mTULineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_TABLE), m_ui->tableLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_FWMARK), m_ui->fwMarkLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_PRESHARED_KEY), m_ui->presharedKeyLineEdit->text()); + setOrClear(data, QLatin1String(NM_WG_KEY_PRE_UP), m_ui->preUpScriptLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_POST_UP), m_ui->postUpScriptLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_PRE_DOWN), m_ui->preDownScriptLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_POST_DOWN), m_ui->postDownScriptLineEdit->displayText()); + + d->setting->setData(data); + + return d->setting; +} diff --git a/vpn/wireguard/wireguardauth.h b/vpn/wireguard/wireguardauth.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardauth.h @@ -0,0 +1,42 @@ +/* + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef WIREGUARDAUTH_H +#define WIREGUARDAUTH_H + +#include + +#include "settingwidget.h" + +class WireGuardAuthWidgetPrivate; + +class WireGuardAuthWidget : public SettingWidget +{ + Q_OBJECT + Q_DECLARE_PRIVATE(WireGuardAuthWidget) +public: + explicit WireGuardAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = 0); + ~WireGuardAuthWidget(); + virtual QVariantMap setting() const; + +private: + WireGuardAuthWidgetPrivate *const d_ptr; +}; + +#endif // WIREGUARDAUTH_H diff --git a/vpn/wireguard/wireguardauth.cpp b/vpn/wireguard/wireguardauth.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardauth.cpp @@ -0,0 +1,55 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "wireguardauth.h" +#include "ui_wireguardauth.h" + +#include + +class WireGuardAuthWidgetPrivate +{ +public: + Ui_WireGuardAuth ui; + NetworkManager::VpnSetting::Ptr setting; +}; + +WireGuardAuthWidget::WireGuardAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) + : SettingWidget(setting, parent) + , d_ptr(new WireGuardAuthWidgetPrivate) +{ + Q_D(WireGuardAuthWidget); + d->ui.setupUi(this); + d->setting = setting; + + KAcceleratorManager::manage(this); +} + +WireGuardAuthWidget::~WireGuardAuthWidget() +{ + delete d_ptr; +} + + + +QVariantMap WireGuardAuthWidget::setting() const +{ + Q_D(const WireGuardAuthWidget); + + QVariantMap result; + return result; +} diff --git a/vpn/wireguard/wireguardauth.ui b/vpn/wireguard/wireguardauth.ui new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardauth.ui @@ -0,0 +1,31 @@ + + + WireGuardAuth + + + + 0 + 0 + 263 + 51 + + + + + 6 + + + + + No WireGuard Secrets currently + + + false + + + + + + + + diff --git a/vpn/wireguard/wireguardutils.h b/vpn/wireguard/wireguardutils.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardutils.h @@ -0,0 +1,29 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include + +class WireGuardUtils +{ +public: + static bool is_num_valid(QString candidate, int min=0, int max=0); + static bool is_ip4(QString addr, bool allow_subnet=true, bool allow_port=true); + static bool is_ip6(QString addr, bool allow_subnet=true); + static bool is_key_valid(QString candidate); +}; diff --git a/vpn/wireguard/wireguardutils.cpp b/vpn/wireguard/wireguardutils.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardutils.cpp @@ -0,0 +1,250 @@ +/* + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "wireguardutils.h" + +// Checks to see if a string contains only number characters. +// If min and max are given, will check to see if the number is in min <= number <= max +bool WireGuardUtils::is_num_valid(QString candidate, int min, int max) +{ + if (candidate.length() == 0 || + candidate.indexOf(QRegExp("[^0-9]")) > -1 || + (candidate.toInt() > max && max != min) || + (candidate.toInt() < min && max != min)) + { + return false; + } + else + { + return true; + } +} + +// check if the given string looks like an IPv4 address +// that is, four segments of numbers (0-255), separated by dots +// additionally, there may be a port suffix (separated from the address by a colon; 0 - 65535) +// and/or a subnet (separated by the rest by a slash; 0 - 32) +bool WireGuardUtils::is_ip4(QString addr, bool allow_subnet, bool allow_port) +{ + int idx = 0; + QStringList parts; + QStringList lastpart; + + // If we got an empty string, fail + if (0 == addr.length()) + { + return false; + } + + // Split up the string at the dots + parts << addr.split("."); + + // If there weren't 4 parts, fail + if(parts.length() != 4) + { + return false; + } + + // iterate over the first three parts, which cannot be anything else than numbers + for(idx = 0; idx < 3; idx++) + { + // Check that each "chunk" contains at least one character, is all numbers, and is less than <= 255 + if (!is_num_valid(parts[idx], 0 , 255)) + { + return false; + } + } + + // the last part might be just a number less than 255 or + // have a subnet suffix after a slash (e.g. 192.168.1.254/24) + // might have a port suffix after a colon (e.g. 192.168.1.254:8080) + // or both: 192.168.2.1:808/24 + + // First check that subnet and port are allowed + if ((parts[3].contains("/") && false == allow_subnet) || + (parts[3].contains(":") && false == allow_port)) + { + return false; + } + + // Split on both "/" and ":" to see if it contains either a subnet mask + // or a port + lastpart = parts[3].split(QRegExp("[/:]")); + + // test the last octet + if (!is_num_valid(lastpart[0], 0, 255)) + { + return false; + } + + // If there isn't either a netmask or a port, we're done + if (lastpart.length() == 1) + { + return true; + } + + // Split off and test the netmask if there is one + lastpart = parts[3].split("/"); + + if (lastpart.length() == 2 && + (!is_num_valid(lastpart[1], 0, 32))) + { + return false; + } + + // Split off and test the port if there is one + lastpart = lastpart[0].split(":"); + + if (lastpart.length() == 2 && + (!is_num_valid(lastpart[1], 0, 65535))) + { + return false; + } + + return true; +} + +// check if the given string looks like an IPv6 address +// that is, eight segments of hex numbers (00-ff), separated by colons +// additionally, there may be a subnet (separated by the rest by a slash; 0 - 128) +bool WireGuardUtils::is_ip6(QString addr, bool allow_subnet) +{ + QStringList parts; + QStringList lastpart; + QString subnet; + int num_parts; + int part_length[8]; + int num_empty = 0; + bool has_subnet = false; + + // If we got an empty string, fail + if (0 == addr.length()) + { + return false; + } + + // Split up the string at the cololn + parts << addr.split(":"); + num_parts = parts.size(); + + // If there aren't at least 2 parts, and at most 8 fail + if(num_parts < 3 || num_parts > 8) + { + return false; + } + + // Separate out the last (possibly blank) hextet and the netmask if it has one + lastpart = parts[num_parts-1].split("/"); + if (lastpart.size() > 1) + { + if (false == allow_subnet) + { + return false; + } + has_subnet = true; + subnet = lastpart[1]; + if (0 == subnet.length()) + { + return false; + } + parts[num_parts-1] = lastpart[0]; + } + + // Count the number of blank fields + for (int i = 0; i < num_parts; i++) + { + part_length[i] = parts[i].length(); + if (part_length[i] == 0) + { + num_empty++; + } + } + + // If there are more than 3 empty hextets it's an error + if (num_empty > 3) + { + return false; + } + + // If there are 3 empty hextets there must only be the 3 (i.e. ::) + else if (num_empty == 3 && num_parts != 3) + { + return false; + } + + // if there are 2 empty hextets they must either be the first 2 or last 2 + else if (num_empty == 2 && + !(part_length[0] == 0 && part_length[1] == 0) && + !(part_length[num_parts-1] == 0 && part_length[num_parts-2] == 0)) + { + return false; + } + + // If there is just 1 empty it must not be first or last + else if (num_empty == 1 && + (0 == part_length[0] || 0 == part_length[num_parts-1])) + { + return false; + } + + // If there are less than 8 parts there must be at least 1 empty + if (num_parts < 8 && (0 == num_empty || (1 == num_empty && 0 == part_length[num_parts-1]))) + { + return false; + } + + // Now just check that the hextets is valid + for (int i = 0; i < num_parts; i++) + { + if (0 == part_length[i]) + { + continue; + } + else if (part_length[i] > 4) + { + return false; + } + else if (parts[i].indexOf(QRegExp("[^0-9A-Fa-f]")) > -1) + { + return false; + } + } + + // Check the netmask + if (has_subnet && !is_num_valid(subnet, 0, 128)) + { + return false; + } + + return true; +} + +// Check if a string is a valid WireGuard key which should be +// 44 characters long and composed of alphanumeric characters, plus +// sign, and slash with the last one being an equal sign. +bool WireGuardUtils::is_key_valid(QString candidate) +{ + if (44 != candidate.length() || + 43 != candidate.indexOf(QRegExp("[^a-zA-Z0-9+/]")) || + 43 != candidate.indexOf("=")) + { + return false; + } + + return true; +} diff --git a/vpn/wireguard/wireguardwidget.h b/vpn/wireguard/wireguardwidget.h new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardwidget.h @@ -0,0 +1,64 @@ +/* + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef WIREGUARDWIDGET_H +#define WIREGUARDWIDGET_H + +#include "settingwidget.h" + +#include + +#include "ui_wireguard.h" + +#include + +class QUrl; +class QLineEdit; + +class WireGuardSettingWidget : public SettingWidget +{ + Q_OBJECT +public: + explicit WireGuardSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = 0); + ~WireGuardSettingWidget(); + + void loadConfig(const NetworkManager::Setting::Ptr &setting) Q_DECL_OVERRIDE; + void loadSecrets(const NetworkManager::Setting::Ptr &setting) Q_DECL_OVERRIDE; + + QVariantMap setting() const; + + virtual bool isValid() const; + +private Q_SLOTS: + void showAdvanced(); + +private: + class Private; + Private *d; + bool isAddressValid() const; + bool isPrivateKeyValid() const; + bool isDNSValid() const; + bool isPublicKeyValid() const; + bool isAllowedIPsValid() const; + bool isEndpointValid() const; + void setOrClear(NMStringMap &data, QLatin1String key, QString value) const; + +}; + +#endif // WIREGUARDWIDGET_H diff --git a/vpn/wireguard/wireguardwidget.cpp b/vpn/wireguard/wireguardwidget.cpp new file mode 100644 --- /dev/null +++ b/vpn/wireguard/wireguardwidget.cpp @@ -0,0 +1,304 @@ +/* + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "debug.h" +#include "wireguardwidget.h" +#include "wireguardadvancedwidget.h" +#include "wireguardutils.h" + +#include +#include +#include +#include + +#include +#include + +#include "nm-wireguard-service.h" + +class WireGuardSettingWidget::Private +{ +public: + Ui_WireGuardProp ui; + NetworkManager::VpnSetting::Ptr setting; +}; + + +WireGuardSettingWidget::WireGuardSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) + : SettingWidget(setting, parent) + , d(new Private) +{ + qDBusRegisterMetaType(); + + d->ui.setupUi(this); + d->setting = setting; + + connect(d->ui.addressIPv4LineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isAddressValid); + connect(d->ui.addressIPv6LineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isAddressValid); + connect(d->ui.privateKeyLineEdit, &PasswordField::textChanged, this, &WireGuardSettingWidget::isPrivateKeyValid); + connect(d->ui.dNSLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isDNSValid); + connect(d->ui.publicKeyLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isPublicKeyValid); + connect(d->ui.allowedIPsLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isAllowedIPsValid); + connect(d->ui.endpointLineEdit, &QLineEdit::textChanged, this, &WireGuardSettingWidget::isEndpointValid); + + d->ui.privateKeyLineEdit->setPasswordModeEnabled(true); + + connect(d->ui.btnAdvanced, &QPushButton::clicked, this, &WireGuardSettingWidget::showAdvanced); + + + // Connect for setting check + watchChangedSetting(); + + KAcceleratorManager::manage(this); + + if (setting && !setting->isNull()) + { + loadConfig(d->setting); + } + else + { + isAddressValid(); + isPrivateKeyValid(); + isDNSValid(); + isPublicKeyValid(); + isAllowedIPsValid(); + isEndpointValid(); + } +} + +WireGuardSettingWidget::~WireGuardSettingWidget() +{ + delete d; +} + +void WireGuardSettingWidget::loadConfig(const NetworkManager::Setting::Ptr &setting) +{ + Q_UNUSED(setting) + // General settings + const NMStringMap dataMap = d->setting->data(); + + d->ui.addressIPv4LineEdit->setText(dataMap[NM_WG_KEY_ADDR_IP4]); + d->ui.addressIPv6LineEdit->setText(dataMap[NM_WG_KEY_ADDR_IP6]); + d->ui.privateKeyLineEdit->setText(dataMap[NM_WG_KEY_PRIVATE_KEY]); + d->ui.dNSLineEdit->setText(dataMap[NM_WG_KEY_DNS]); + d->ui.publicKeyLineEdit->setText(dataMap[NM_WG_KEY_PUBLIC_KEY]); + d->ui.allowedIPsLineEdit->setText(dataMap[NM_WG_KEY_ALLOWED_IPS]); + d->ui.endpointLineEdit->setText(dataMap[NM_WG_KEY_ENDPOINT]); + +} + +void WireGuardSettingWidget::loadSecrets(const NetworkManager::Setting::Ptr &setting) +{ + // Currently WireGuard does not have any secrets +} + +QVariantMap WireGuardSettingWidget::setting() const +{ + NMStringMap data = d->setting->data(); + NetworkManager::VpnSetting setting; + setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_WIREGUARD)); + + // required settings + + setOrClear(data, QLatin1String(NM_WG_KEY_ADDR_IP4), d->ui.addressIPv4LineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_ADDR_IP6), d->ui.addressIPv6LineEdit->displayText()); + + setOrClear(data, QLatin1String(NM_WG_KEY_PRIVATE_KEY), d->ui.privateKeyLineEdit->text()); + setOrClear(data, QLatin1String(NM_WG_KEY_PUBLIC_KEY), d->ui.publicKeyLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_ALLOWED_IPS), d->ui.allowedIPsLineEdit->displayText()); + + setOrClear(data, QLatin1String(NM_WG_KEY_DNS), d->ui.dNSLineEdit->displayText()); + setOrClear(data, QLatin1String(NM_WG_KEY_ENDPOINT), d->ui.endpointLineEdit->displayText()); + + setting.setData(data); + + return setting.toMap(); +} + +void WireGuardSettingWidget::setOrClear(NMStringMap &data, QLatin1String key, QString value) const +{ + if (0 != value.length()) + { + data.insert(key, value); + } + else + { + data.remove(key); + } +} + +void WireGuardSettingWidget::showAdvanced() +{ + QPointer adv = new WireGuardAdvancedWidget(d->setting, this); + + connect(adv.data(), &WireGuardAdvancedWidget::accepted, + [adv, this] () { + NetworkManager::VpnSetting::Ptr advData = adv->setting(); + if (!advData.isNull()) { + d->setting->setData(advData->data()); + } + }); + connect(adv.data(), &WireGuardAdvancedWidget::finished, + [adv] () { + if (adv) { + adv->deleteLater(); + } + }); + adv->setModal(true); + adv->show(); +} + +bool WireGuardSettingWidget::isValid() const +{ + return (isAddressValid() && + isPrivateKeyValid() && + isDNSValid() && + isPublicKeyValid() && + isAllowedIPsValid() && + isEndpointValid()); +} + +bool WireGuardSettingWidget::isAddressValid() const +{ + bool ip4valid = WireGuardUtils::is_ip4(d->ui.addressIPv4LineEdit->displayText(), true, false); + bool ip4present = (d->ui.addressIPv4LineEdit->displayText().length() != 0); + bool ip6valid = WireGuardUtils::is_ip6(d->ui.addressIPv6LineEdit->displayText(), true); + bool ip6present = (d->ui.addressIPv6LineEdit->displayText().length() != 0); + + bool result = (ip4valid && ip6valid) || + (ip4valid && !ip6present) || + (!ip4present && ip6valid); + + if (!result) + { + d->ui.addressIPv4LineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + d->ui.addressIPv6LineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.addressIPv4LineEdit->setStyleSheet("* { background-color: }"); + d->ui.addressIPv6LineEdit->setStyleSheet("* { background-color: }"); + } + return result; +} + +bool WireGuardSettingWidget::isPrivateKeyValid() const +{ + bool valid = WireGuardUtils::is_key_valid(d->ui.privateKeyLineEdit->text()); + + if (!valid) + { + d->ui.privateKeyLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.privateKeyLineEdit->setStyleSheet("* { background-color: }"); + } + return valid; +} + +bool WireGuardSettingWidget::isDNSValid() const +{ + bool valid = WireGuardUtils::is_ip4(d->ui.dNSLineEdit->displayText(), false, false); + bool present = (0 != d->ui.dNSLineEdit->displayText().length()); + bool result = valid || !present; + + if (!result) + { + d->ui.dNSLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.dNSLineEdit->setStyleSheet("* { background-color: }"); + } + + return result; +} + +bool WireGuardSettingWidget::isPublicKeyValid() const +{ + bool valid = WireGuardUtils::is_key_valid(d->ui.publicKeyLineEdit->text()); + + if (!valid) + { + d->ui.publicKeyLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.publicKeyLineEdit->setStyleSheet("* { background-color: }"); + } + return valid; +} + +bool WireGuardSettingWidget::isAllowedIPsValid() const +{ + bool result = true; + bool present = (0 != d->ui.allowedIPsLineEdit->displayText().length()); + + if (present) + { + // Split the string on commas + QStringList addrs = d->ui.allowedIPsLineEdit->displayText().split(QRegExp("\\s*,\\s*")); + + for (int i = 0; i < addrs.size(); i++) + { + if (!WireGuardUtils::is_ip4(addrs[i], true, false) && !WireGuardUtils::is_ip6(addrs[i], true)) + { + result = false; + } + if (!addrs[i].contains("/")) + { + result = false; + } + } + } + else + { + result = false; + } + + if (!result) + { + d->ui.allowedIPsLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.allowedIPsLineEdit->setStyleSheet("* { background-color: }"); + } + + return result; +} + +bool WireGuardSettingWidget::isEndpointValid() const +{ + bool valid = WireGuardUtils::is_ip4(d->ui.endpointLineEdit->displayText(), false, true); + bool present = (0 != d->ui.endpointLineEdit->displayText().length()); + bool result = !present || (valid && d->ui.endpointLineEdit->displayText().contains(":")); + + if (!result) + { + d->ui.endpointLineEdit->setStyleSheet("* { background-color: rgb(255,128, 128) }"); + } + else + { + d->ui.endpointLineEdit->setStyleSheet("* { background-color: }"); + } + + return result; +}