File indexing completed on 2024-07-21 07:58:06

0001 /*
0002     SPDX-FileCopyrightText: 2019 Bruce Anderson <banderson19com@san.rr.com>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "wireguardpeerwidget.h"
0007 #include "simpleiplistvalidator.h"
0008 #include "simpleipv4addressvalidator.h"
0009 #include "ui_wireguardpeerwidget.h"
0010 #include "wireguardkeyvalidator.h"
0011 
0012 #include <QStandardItemModel>
0013 #include <QUrl>
0014 
0015 #include <KColorScheme>
0016 #include <KConfig>
0017 #include <KConfigGroup>
0018 #include <NetworkManagerQt/Ipv4Setting>
0019 #include <NetworkManagerQt/Ipv6Setting>
0020 #include <NetworkManagerQt/Utils>
0021 
0022 // Keys for the NetworkManager configuration
0023 #define PNM_SETTING_WIREGUARD_SETTING_NAME "wireguard"
0024 
0025 #define PNM_WG_KEY_PEERS "peers"
0026 #define PNM_WG_KEY_MTU "mtu"
0027 #define PNM_WG_KEY_PEER_ROUTES "peer-routes"
0028 #define PNM_WG_PEER_KEY_ALLOWED_IPS "allowed-ips"
0029 #define PNM_WG_PEER_KEY_ENDPOINT "endpoint"
0030 #define PNM_WG_PEER_KEY_PERSISTENT_KEEPALIVE "persistent-keepalive"
0031 #define PNM_WG_PEER_KEY_PRESHARED_KEY "preshared-key"
0032 #define PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS "preshared-key-flags"
0033 #define PNM_WG_PEER_KEY_PUBLIC_KEY "public-key"
0034 
0035 static WireGuardKeyValidator keyValidator;
0036 static SimpleIpListValidator allowedIPsValidator(SimpleIpListValidator::WithCidr, SimpleIpListValidator::Both);
0037 
0038 class WireGuardPeerWidget::Private
0039 {
0040 public:
0041     ~Private();
0042 
0043     Ui_WireGuardPeersProp ui;
0044     NetworkManager::WireGuardSetting::Ptr setting;
0045     KSharedConfigPtr config;
0046     QPalette warningPalette;
0047     QPalette normalPalette;
0048     QVariantMap peerData;
0049     bool publicKeyValid = false;
0050     bool allowedIPsValid = false;
0051     bool endpointValid = true;
0052     bool presharedKeyValid = true;
0053 };
0054 
0055 WireGuardPeerWidget::Private::~Private() = default;
0056 
0057 WireGuardPeerWidget::WireGuardPeerWidget(const QVariantMap &peerData, QWidget *parent, Qt::WindowFlags f)
0058     : QDialog(parent, f)
0059     , d(new Private)
0060 {
0061     d->ui.setupUi(this);
0062     d->peerData = peerData;
0063 
0064     d->config = KSharedConfig::openConfig();
0065     d->warningPalette = KColorScheme::createApplicationPalette(d->config);
0066     d->normalPalette = KColorScheme::createApplicationPalette(d->config);
0067     KColorScheme::adjustBackground(d->warningPalette, KColorScheme::NegativeBackground, QPalette::Base, KColorScheme::ColorSet::View, d->config);
0068 
0069     KColorScheme::adjustBackground(d->normalPalette, KColorScheme::NormalBackground, QPalette::Base, KColorScheme::ColorSet::View, d->config);
0070 
0071     setWindowTitle(i18nc("@title: window wireguard peers properties", "WireGuard peers properties"));
0072     connect(d->ui.publicKeyLineEdit, &QLineEdit::textChanged, this, &WireGuardPeerWidget::checkPublicKeyValid);
0073     connect(d->ui.allowedIPsLineEdit, &QLineEdit::textChanged, this, &WireGuardPeerWidget::checkAllowedIpsValid);
0074     connect(d->ui.endpointAddressLineEdit, &QLineEdit::textChanged, this, &WireGuardPeerWidget::checkEndpointValid);
0075     connect(d->ui.endpointPortLineEdit, &QLineEdit::textChanged, this, &WireGuardPeerWidget::checkEndpointValid);
0076     connect(d->ui.presharedKeyLineEdit, &PasswordField::textChanged, this, &WireGuardPeerWidget::checkPresharedKeyValid);
0077     connect(d->ui.presharedKeyLineEdit, &PasswordField::passwordOptionChanged, this, &WireGuardPeerWidget::saveKeyFlags);
0078     connect(d->ui.keepaliveLineEdit, &QLineEdit::textChanged, this, &WireGuardPeerWidget::saveKeepAlive);
0079 
0080     d->ui.presharedKeyLineEdit->setPasswordModeEnabled(true);
0081     d->ui.presharedKeyLineEdit->setPasswordOptionsEnabled(true);
0082     d->ui.presharedKeyLineEdit->setPasswordNotRequiredEnabled(true);
0083     d->ui.presharedKeyLineEdit->setPasswordNotSavedEnabled(false);
0084 
0085     // Create validator for endpoint port
0086     auto portValidator = new QIntValidator(this);
0087     portValidator->setBottom(0);
0088     portValidator->setTop(65535);
0089     d->ui.endpointPortLineEdit->setValidator(portValidator);
0090 
0091     // Reuse the port validator for the persistent keepalive.
0092     // It is not a "port" but has the same limits
0093     d->ui.keepaliveLineEdit->setValidator(portValidator);
0094 
0095     KAcceleratorManager::manage(this);
0096     updatePeerWidgets();
0097 
0098     // Set the initial backgrounds on all the widgets
0099     checkPublicKeyValid();
0100     checkAllowedIpsValid();
0101     checkEndpointValid();
0102 }
0103 
0104 WireGuardPeerWidget::~WireGuardPeerWidget()
0105 {
0106     delete d;
0107 }
0108 
0109 QVariantMap WireGuardPeerWidget::setting() const
0110 {
0111     return d->peerData;
0112 }
0113 
0114 void WireGuardPeerWidget::checkPublicKeyValid()
0115 {
0116     int pos = 0;
0117     QLineEdit *widget = d->ui.publicKeyLineEdit;
0118     QString value = widget->displayText();
0119     bool valid = QValidator::Acceptable == keyValidator.validate(value, pos);
0120     setBackground(widget, valid);
0121     d->peerData[PNM_WG_PEER_KEY_PUBLIC_KEY] = value;
0122     if (valid != d->publicKeyValid) {
0123         d->publicKeyValid = valid;
0124         slotWidgetChanged();
0125     }
0126 }
0127 
0128 void WireGuardPeerWidget::checkPresharedKeyValid()
0129 {
0130     int pos = 0;
0131     PasswordField *widget = d->ui.presharedKeyLineEdit;
0132     QString value = widget->text();
0133 
0134     // The value in the preshared key field is ignored if the type
0135     // of password is set to "Ask every time" or "Not Required" so
0136     // it is valid if it is set to "Store for user only"
0137     // or "Store for all users" even if the password is bad
0138     PasswordField::PasswordOption option = d->ui.presharedKeyLineEdit->passwordOption();
0139     bool valid = (QValidator::Acceptable == keyValidator.validate(value, pos) || option == PasswordField::NotRequired);
0140     setBackground(widget, valid);
0141     if (value.isEmpty())
0142         d->peerData.remove(PNM_WG_PEER_KEY_PRESHARED_KEY);
0143     else
0144         d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY] = value;
0145     if (valid != d->presharedKeyValid) {
0146         d->presharedKeyValid = valid;
0147         slotWidgetChanged();
0148     }
0149 }
0150 
0151 void WireGuardPeerWidget::checkAllowedIpsValid()
0152 {
0153     int pos = 0;
0154     QLineEdit *widget = d->ui.allowedIPsLineEdit;
0155     QString ipString = widget->displayText();
0156     QStringList rawIPList = ipString.split(QLatin1Char(','));
0157     QStringList ipList;
0158 
0159     bool valid = QValidator::Acceptable == allowedIPsValidator.validate(ipString, pos);
0160     setBackground(widget, valid);
0161 
0162     ipList.reserve(rawIPList.size());
0163     for (const QString &ip : rawIPList) {
0164         ipList.append(ip.trimmed());
0165     }
0166 
0167     d->peerData[PNM_WG_PEER_KEY_ALLOWED_IPS] = ipList;
0168     if (valid != d->allowedIPsValid) {
0169         d->allowedIPsValid = valid;
0170         slotWidgetChanged();
0171     }
0172 }
0173 
0174 WireGuardPeerWidget::EndPointValid WireGuardPeerWidget::isEndpointValid(QString &address, QString &port)
0175 {
0176     // Create a Reg Expression validator to do a simple check for a valid qualified domain name
0177     // which checks to see that there are between 2 and 63 strings of at least 1 character each
0178     // separated by '.'. The full string must also be less than 255 characters long.
0179     static QRegularExpressionValidator fqdnValidator(QRegularExpression(QLatin1String("(?=.{3,254}$)([a-zA-Z0-9][a-zA-Z0-9-]{0,62}\\.){1,63}[a-zA-Z]{1,63}")),
0180                                                      nullptr);
0181     static SimpleIpV4AddressValidator ipv4Validator;
0182     static SimpleIpV6AddressValidator ipv6Validator;
0183     int pos = 0;
0184 
0185     bool addressValid = QValidator::Acceptable == fqdnValidator.validate(address, pos) || QValidator::Acceptable == ipv4Validator.validate(address, pos)
0186         || QValidator::Acceptable == ipv6Validator.validate(address, pos);
0187     bool bothEmpty = address.isEmpty() && port.isEmpty();
0188     // Because of the validator, if the port is non-empty, it is valid
0189     bool portValid = !port.isEmpty();
0190 
0191     if ((portValid && addressValid) || bothEmpty)
0192         return WireGuardPeerWidget::BothValid;
0193     else if (portValid)
0194         return WireGuardPeerWidget::PortValid;
0195     else if (addressValid)
0196         return WireGuardPeerWidget::AddressValid;
0197     else
0198         return WireGuardPeerWidget::BothInvalid;
0199 }
0200 
0201 void WireGuardPeerWidget::checkEndpointValid()
0202 {
0203     QLineEdit *addressWidget = d->ui.endpointAddressLineEdit;
0204     QLineEdit *portWidget = d->ui.endpointPortLineEdit;
0205     QString addressString = addressWidget->displayText();
0206     QString portString = portWidget->displayText();
0207 
0208     WireGuardPeerWidget::EndPointValid valid = isEndpointValid(addressString, portString);
0209 
0210     setBackground(addressWidget, WireGuardPeerWidget::BothValid == valid || WireGuardPeerWidget::AddressValid == valid);
0211     setBackground(portWidget, WireGuardPeerWidget::BothValid == valid || WireGuardPeerWidget::PortValid == valid);
0212 
0213     // If there is a ':' in the address string then it is an IPv6 address and
0214     // the output needs to be formatted as '[1:2:3:4:5:6:7:8]:123' otherwise
0215     // it is formatted as '1.2.3.4:123' or 'ab.com:123'
0216     QString stringToStore;
0217     if (addressString.contains(":"))
0218         stringToStore = "[" + addressString.trimmed() + "]:" + portString.trimmed();
0219     else
0220         stringToStore = addressString.trimmed() + ":" + portString.trimmed();
0221 
0222     if (addressString.isEmpty() && portString.isEmpty())
0223         d->peerData.remove(PNM_WG_PEER_KEY_ENDPOINT);
0224     else
0225         d->peerData[PNM_WG_PEER_KEY_ENDPOINT] = stringToStore;
0226 
0227     if ((valid == WireGuardPeerWidget::BothValid) != d->endpointValid) {
0228         d->endpointValid = (valid == WireGuardPeerWidget::BothValid);
0229         slotWidgetChanged();
0230     }
0231 }
0232 
0233 bool WireGuardPeerWidget::isValid()
0234 {
0235     return d->publicKeyValid && d->allowedIPsValid && d->endpointValid && d->presharedKeyValid;
0236 }
0237 
0238 void WireGuardPeerWidget::saveKeepAlive()
0239 {
0240     QLineEdit *widget = d->ui.keepaliveLineEdit;
0241     QString value = widget->displayText();
0242 
0243     if (value.isEmpty())
0244         d->peerData.remove(PNM_WG_PEER_KEY_PERSISTENT_KEEPALIVE);
0245     else
0246         d->peerData[PNM_WG_PEER_KEY_PERSISTENT_KEEPALIVE] = value;
0247 }
0248 
0249 void WireGuardPeerWidget::saveKeyFlags()
0250 {
0251     PasswordField::PasswordOption option = d->ui.presharedKeyLineEdit->passwordOption();
0252     switch (option) {
0253     case PasswordField::StoreForUser:
0254         d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS] = NetworkManager::Setting::AgentOwned;
0255         break;
0256     case PasswordField::StoreForAllUsers:
0257         d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS] = NetworkManager::Setting::None;
0258         break;
0259     // Always Ask is not a valid option for the preshared key so set it to AgentOwned instead
0260     case PasswordField::AlwaysAsk:
0261         d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS] = NetworkManager::Setting::AgentOwned;
0262         break;
0263     case PasswordField::NotRequired:
0264         d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS] = NetworkManager::Setting::NotRequired;
0265         break;
0266     }
0267     checkPresharedKeyValid();
0268 }
0269 
0270 void WireGuardPeerWidget::setBackground(QWidget *w, bool result) const
0271 {
0272     if (result)
0273         w->setPalette(d->normalPalette);
0274     else
0275         w->setPalette(d->warningPalette);
0276 }
0277 
0278 void WireGuardPeerWidget::updatePeerWidgets()
0279 {
0280     d->ui.presharedKeyLineEdit->setPasswordModeEnabled(true);
0281 
0282     if (d->peerData.contains(PNM_WG_PEER_KEY_PUBLIC_KEY))
0283         d->ui.publicKeyLineEdit->setText(d->peerData[PNM_WG_PEER_KEY_PUBLIC_KEY].toString());
0284     else
0285         d->ui.publicKeyLineEdit->clear();
0286 
0287     if (d->peerData.contains(PNM_WG_PEER_KEY_ALLOWED_IPS)) {
0288         QStringList allowedIps = d->peerData[PNM_WG_PEER_KEY_ALLOWED_IPS].toStringList();
0289         d->ui.allowedIPsLineEdit->setText(allowedIps.join(","));
0290     } else {
0291         d->ui.allowedIPsLineEdit->clear();
0292     }
0293 
0294     if (d->peerData.contains(PNM_WG_PEER_KEY_PERSISTENT_KEEPALIVE))
0295         d->ui.keepaliveLineEdit->setText(d->peerData[PNM_WG_PEER_KEY_PERSISTENT_KEEPALIVE].toString());
0296     else
0297         d->ui.keepaliveLineEdit->clear();
0298 
0299     // An endpoint is stored as <ipv4 | [ipv6] | fqdn>:<port>
0300     if (d->peerData.contains(PNM_WG_PEER_KEY_ENDPOINT)) {
0301         QString storedEndpoint = d->peerData[PNM_WG_PEER_KEY_ENDPOINT].toString();
0302         QStringList endpointList = storedEndpoint.contains("]:") ? d->peerData[PNM_WG_PEER_KEY_ENDPOINT].toString().split("]:")
0303                                                                  : d->peerData[PNM_WG_PEER_KEY_ENDPOINT].toString().split(":");
0304 
0305         d->ui.endpointAddressLineEdit->setText(endpointList[0].remove("["));
0306         d->ui.endpointPortLineEdit->setText(endpointList[1]);
0307     } else {
0308         d->ui.endpointAddressLineEdit->clear();
0309         d->ui.endpointPortLineEdit->clear();
0310     }
0311     if (d->peerData.contains(PNM_WG_PEER_KEY_PRESHARED_KEY))
0312         d->ui.presharedKeyLineEdit->setText(d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY].toString());
0313     else
0314         d->ui.presharedKeyLineEdit->setText("");
0315 
0316     if (d->peerData.contains(PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS)) {
0317         NetworkManager::Setting::SecretFlags type =
0318             static_cast<NetworkManager::Setting::SecretFlags>(d->peerData[PNM_WG_PEER_KEY_PRESHARED_KEY_FLAGS].toUInt());
0319         switch (type) {
0320         case NetworkManager::Setting::AgentOwned:
0321             d->ui.presharedKeyLineEdit->setPasswordOption(PasswordField::StoreForUser);
0322             break;
0323         case NetworkManager::Setting::None:
0324             d->ui.presharedKeyLineEdit->setPasswordOption(PasswordField::StoreForAllUsers);
0325             break;
0326         // Not saved is not a valid option for the private key so set it to StoreForUser instead
0327         case NetworkManager::Setting::NotSaved:
0328             d->ui.presharedKeyLineEdit->setPasswordOption(PasswordField::StoreForUser);
0329             break;
0330         case NetworkManager::Setting::NotRequired:
0331             d->ui.presharedKeyLineEdit->setPasswordOption(PasswordField::NotRequired);
0332             break;
0333         }
0334     } else {
0335         d->ui.presharedKeyLineEdit->setPasswordOption(PasswordField::NotRequired);
0336     }
0337 
0338     slotWidgetChanged();
0339 }
0340 
0341 void WireGuardPeerWidget::slotWidgetChanged()
0342 {
0343     Q_EMIT notifyValid();
0344 }
0345 
0346 #include "moc_wireguardpeerwidget.cpp"