File indexing completed on 2025-04-27 05:08:12
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"