File indexing completed on 2024-04-21 16:20:26

0001 /*
0002     SPDX-FileCopyrightText: 2008 Will Stephenson <wstephenson@kde.org>
0003     SPDX-FileCopyrightText: 2013 Lukáš Tinkl <ltinkl@redhat.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0006 */
0007 
0008 #include "openvpnwidget.h"
0009 #include "openvpnadvancedwidget.h"
0010 #include "plasma_nm_openvpn.h"
0011 
0012 #include <QDBusMetaType>
0013 #include <QLineEdit>
0014 #include <QPointer>
0015 #include <QUrl>
0016 
0017 #include <KProcess>
0018 #include <KUrlRequester>
0019 
0020 #include "nm-openvpn-service.h"
0021 
0022 class OpenVpnSettingWidget::Private
0023 {
0024 public:
0025     Ui_OpenVPNProp ui;
0026     NetworkManager::VpnSetting::Ptr setting;
0027     class EnumConnectionType
0028     {
0029     public:
0030         enum ConnectionType { Certificates = 0, Psk, Password, CertsPassword };
0031     };
0032     class EnumKeyDirection
0033     {
0034     public:
0035         enum KeyDirection { None = 0, D0, D1 };
0036     };
0037 };
0038 
0039 OpenVpnSettingWidget::OpenVpnSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent)
0040     : SettingWidget(setting, parent)
0041     , d(new Private)
0042 {
0043     qDBusRegisterMetaType<NMStringMap>();
0044 
0045     d->ui.setupUi(this);
0046     d->setting = setting;
0047 
0048     d->ui.x509KeyPassword->setPasswordOptionsEnabled(true);
0049     d->ui.x509KeyPassword->setPasswordNotRequiredEnabled(true);
0050     d->ui.passPassword->setPasswordOptionsEnabled(true);
0051     d->ui.passPassword->setPasswordNotRequiredEnabled(true);
0052     d->ui.x509PassKeyPassword->setPasswordOptionsEnabled(true);
0053     d->ui.x509PassKeyPassword->setPasswordNotRequiredEnabled(true);
0054     d->ui.x509PassPassword->setPasswordOptionsEnabled(true);
0055     d->ui.x509PassPassword->setPasswordNotRequiredEnabled(true);
0056 
0057     // use requesters' urlSelected signals to set other requester's startDirs to save clicking
0058     // around the filesystem
0059     QList<const KUrlRequester *> requesters{
0060         d->ui.x509CaFile,
0061         d->ui.x509Cert,
0062         d->ui.x509Key,
0063         d->ui.pskSharedKey,
0064         d->ui.passCaFile,
0065         d->ui.x509PassCaFile,
0066         d->ui.x509PassCert,
0067         d->ui.x509PassKey,
0068     };
0069     for (const KUrlRequester *requester : requesters) {
0070         connect(requester, &KUrlRequester::urlSelected, this, &OpenVpnSettingWidget::updateStartDir);
0071     }
0072 
0073     connect(d->ui.btnAdvanced, &QPushButton::clicked, this, &OpenVpnSettingWidget::showAdvanced);
0074 
0075     // Connect for setting check
0076     watchChangedSetting();
0077 
0078     // Connect for validity check
0079     connect(d->ui.gateway, &QLineEdit::textChanged, this, &OpenVpnSettingWidget::slotWidgetChanged);
0080 
0081     KAcceleratorManager::manage(this);
0082 
0083     if (setting && !setting->isNull()) {
0084         loadConfig(d->setting);
0085     }
0086 }
0087 
0088 OpenVpnSettingWidget::~OpenVpnSettingWidget()
0089 {
0090     delete d;
0091 }
0092 
0093 void OpenVpnSettingWidget::loadConfig(const NetworkManager::Setting::Ptr &setting)
0094 {
0095     Q_UNUSED(setting)
0096 
0097     // General settings
0098     const NMStringMap dataMap = d->setting->data();
0099     const QString cType = dataMap.value(NM_OPENVPN_KEY_CONNECTION_TYPE);
0100 
0101     if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
0102         d->ui.cmbConnectionType->setCurrentIndex(Private::EnumConnectionType::CertsPassword);
0103         d->ui.x509PassUsername->setText(dataMap[NM_OPENVPN_KEY_USERNAME]);
0104         d->ui.x509PassCaFile->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_CA]));
0105         d->ui.x509PassCert->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_CERT]));
0106         d->ui.x509PassKey->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_KEY]));
0107     } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_STATIC_KEY)) {
0108         d->ui.cmbConnectionType->setCurrentIndex(Private::EnumConnectionType::Psk);
0109         d->ui.pskSharedKey->setText(dataMap[NM_OPENVPN_KEY_STATIC_KEY]);
0110         if (dataMap.contains(NM_OPENVPN_KEY_STATIC_KEY_DIRECTION)) {
0111             switch (dataMap[NM_OPENVPN_KEY_STATIC_KEY_DIRECTION].toUInt()) {
0112             case 0:
0113                 d->ui.cmbKeyDirection->setCurrentIndex(Private::EnumKeyDirection::D0);
0114                 break;
0115             case 1:
0116                 d->ui.cmbKeyDirection->setCurrentIndex(Private::EnumKeyDirection::D1);
0117                 break;
0118             }
0119         } else {
0120             d->ui.cmbKeyDirection->setCurrentIndex(Private::EnumKeyDirection::None);
0121         }
0122         d->ui.pskRemoteIp->setText(dataMap[NM_OPENVPN_KEY_REMOTE_IP]);
0123         d->ui.pskLocalIp->setText(dataMap[NM_OPENVPN_KEY_LOCAL_IP]);
0124     } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD)) {
0125         d->ui.cmbConnectionType->setCurrentIndex(Private::EnumConnectionType::Password);
0126         d->ui.passUserName->setText(dataMap[NM_OPENVPN_KEY_USERNAME]);
0127         d->ui.passCaFile->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_CA]));
0128     } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_TLS)) {
0129         d->ui.cmbConnectionType->setCurrentIndex(Private::EnumConnectionType::Certificates);
0130         d->ui.x509CaFile->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_CA]));
0131         d->ui.x509Cert->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_CERT]));
0132         d->ui.x509Key->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENVPN_KEY_KEY]));
0133     }
0134 
0135     d->ui.gateway->setText(dataMap[NM_OPENVPN_KEY_REMOTE]);
0136 
0137     NetworkManager::Setting::SecretFlags type;
0138 
0139     if (cType == QLatin1String(NM_OPENVPN_CONTYPE_TLS)) {
0140         type = (NetworkManager::Setting::SecretFlags)dataMap[NM_OPENVPN_KEY_CERTPASS "-flags"].toInt();
0141         fillOnePasswordCombo(d->ui.x509KeyPassword, type);
0142     } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD)) {
0143         type = (NetworkManager::Setting::SecretFlags)dataMap[NM_OPENVPN_KEY_PASSWORD "-flags"].toInt();
0144         fillOnePasswordCombo(d->ui.passPassword, type);
0145     } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
0146         type = (NetworkManager::Setting::SecretFlags)dataMap[NM_OPENVPN_KEY_PASSWORD "-flags"].toInt();
0147         fillOnePasswordCombo(d->ui.x509PassPassword, type);
0148         type = (NetworkManager::Setting::SecretFlags)dataMap[NM_OPENVPN_KEY_CERTPASS "-flags"].toInt();
0149         fillOnePasswordCombo(d->ui.x509PassKeyPassword, type);
0150     }
0151 
0152     loadSecrets(setting);
0153 }
0154 
0155 void OpenVpnSettingWidget::loadSecrets(const NetworkManager::Setting::Ptr &setting)
0156 {
0157     NetworkManager::VpnSetting::Ptr vpnSetting = setting.staticCast<NetworkManager::VpnSetting>();
0158 
0159     if (vpnSetting) {
0160         const QString cType = d->setting->data().value(NM_OPENVPN_KEY_CONNECTION_TYPE);
0161         const NMStringMap secrets = vpnSetting->secrets();
0162 
0163         if (cType == QLatin1String(NM_OPENVPN_CONTYPE_TLS)) {
0164             d->ui.x509KeyPassword->setText(secrets.value(NM_OPENVPN_KEY_CERTPASS));
0165         } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD)) {
0166             d->ui.passPassword->setText(secrets.value(NM_OPENVPN_KEY_PASSWORD));
0167         } else if (cType == QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD_TLS)) {
0168             d->ui.x509PassPassword->setText(secrets.value(NM_OPENVPN_KEY_PASSWORD));
0169             d->ui.x509PassKeyPassword->setText(secrets.value(NM_OPENVPN_KEY_CERTPASS));
0170         }
0171     }
0172 }
0173 
0174 QVariantMap OpenVpnSettingWidget::setting() const
0175 {
0176     NMStringMap data = d->setting->data();
0177     NMStringMap secretData = d->setting->secrets();
0178     NetworkManager::VpnSetting setting;
0179     setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_OPENVPN));
0180     // required settings
0181     data.insert(QLatin1String(NM_OPENVPN_KEY_REMOTE), d->ui.gateway->text());
0182 
0183     QString contype;
0184 
0185     switch (d->ui.cmbConnectionType->currentIndex()) {
0186     case Private::EnumConnectionType::Certificates:
0187         contype = QLatin1String(NM_OPENVPN_CONTYPE_TLS);
0188         // qCDebug(PLASMA_NM_OPENVPN_LOG) << "saving VPN TLS settings as urls:" << d->ui.x509CaFile->url() << d->ui.x509Cert->url() << d->ui.x509Key->url();
0189         data.insert(QLatin1String(NM_OPENVPN_KEY_CA), d->ui.x509CaFile->url().toLocalFile());
0190         data.insert(QLatin1String(NM_OPENVPN_KEY_CERT), d->ui.x509Cert->url().toLocalFile());
0191         data.insert(QLatin1String(NM_OPENVPN_KEY_KEY), d->ui.x509Key->url().toLocalFile());
0192         // key password
0193         if (!d->ui.x509KeyPassword->text().isEmpty()) {
0194             secretData.insert(QLatin1String(NM_OPENVPN_KEY_CERTPASS), d->ui.x509KeyPassword->text());
0195         } else {
0196             secretData.remove(QLatin1String(NM_OPENVPN_KEY_CERTPASS));
0197         }
0198         handleOnePasswordType(d->ui.x509KeyPassword, QLatin1String(NM_OPENVPN_KEY_CERTPASS "-flags"), data);
0199         break;
0200     case Private::EnumConnectionType::Psk:
0201         contype = QLatin1String(NM_OPENVPN_CONTYPE_STATIC_KEY);
0202         data.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY), d->ui.pskSharedKey->url().toLocalFile());
0203         switch (d->ui.cmbKeyDirection->currentIndex()) {
0204         case Private::EnumKeyDirection::None:
0205             break;
0206         case Private::EnumKeyDirection::D0:
0207             data.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY_DIRECTION), QString::number(0));
0208             break;
0209         case Private::EnumKeyDirection::D1:
0210             data.insert(QLatin1String(NM_OPENVPN_KEY_STATIC_KEY_DIRECTION), QString::number(1));
0211             break;
0212         }
0213         // ip addresses
0214         data.insert(QLatin1String(NM_OPENVPN_KEY_REMOTE_IP), d->ui.pskRemoteIp->text());
0215         data.insert(QLatin1String(NM_OPENVPN_KEY_LOCAL_IP), d->ui.pskLocalIp->text());
0216         break;
0217     case Private::EnumConnectionType::Password:
0218         contype = QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD);
0219         // username
0220         if (!d->ui.passUserName->text().isEmpty()) {
0221             data.insert(QLatin1String(NM_OPENVPN_KEY_USERNAME), d->ui.passUserName->text());
0222         } else {
0223             data.remove(QLatin1String(NM_OPENVPN_KEY_USERNAME));
0224         }
0225         // password
0226         if (!d->ui.passPassword->text().isEmpty()) {
0227             secretData.insert(QLatin1String(NM_OPENVPN_KEY_PASSWORD), d->ui.passPassword->text());
0228         } else {
0229             secretData.remove(QLatin1String(NM_OPENVPN_KEY_PASSWORD));
0230         }
0231         handleOnePasswordType(d->ui.passPassword, QLatin1String(NM_OPENVPN_KEY_PASSWORD "-flags"), data);
0232         // ca
0233         data.insert(QLatin1String(NM_OPENVPN_KEY_CA), d->ui.passCaFile->url().toLocalFile());
0234         break;
0235     case Private::EnumConnectionType::CertsPassword:
0236         contype = QLatin1String(NM_OPENVPN_CONTYPE_PASSWORD_TLS);
0237         // username
0238         if (!d->ui.x509PassUsername->text().isEmpty()) {
0239             data.insert(QLatin1String(NM_OPENVPN_KEY_USERNAME), d->ui.x509PassUsername->text());
0240         } else {
0241             data.remove(QLatin1String(NM_OPENVPN_KEY_USERNAME));
0242         }
0243         // ca
0244         data.insert(QLatin1String(NM_OPENVPN_KEY_CA), d->ui.x509PassCaFile->url().toLocalFile());
0245         // cert
0246         data.insert(QLatin1String(NM_OPENVPN_KEY_CERT), d->ui.x509PassCert->url().toLocalFile());
0247         // key file
0248         data.insert(QLatin1String(NM_OPENVPN_KEY_KEY), d->ui.x509PassKey->url().toLocalFile());
0249         // key password
0250         if (!d->ui.x509PassKeyPassword->text().isEmpty()) {
0251             secretData.insert(QLatin1String(NM_OPENVPN_KEY_CERTPASS), d->ui.x509PassKeyPassword->text());
0252         } else {
0253             secretData.remove(QLatin1String(NM_OPENVPN_KEY_CERTPASS));
0254         }
0255         handleOnePasswordType(d->ui.x509PassKeyPassword, QLatin1String(NM_OPENVPN_KEY_CERTPASS "-flags"), data);
0256         // password
0257         if (!d->ui.x509PassPassword->text().isEmpty()) {
0258             secretData.insert(QLatin1String(NM_OPENVPN_KEY_PASSWORD), d->ui.x509PassPassword->text());
0259         } else {
0260             secretData.remove(QLatin1String(NM_OPENVPN_KEY_PASSWORD));
0261         }
0262         handleOnePasswordType(d->ui.x509PassPassword, QLatin1String(NM_OPENVPN_KEY_PASSWORD "-flags"), data);
0263         break;
0264     }
0265     data.insert(QLatin1String(NM_OPENVPN_KEY_CONNECTION_TYPE), contype);
0266 
0267     setting.setData(data);
0268     setting.setSecrets(secretData);
0269 
0270     return setting.toMap();
0271 }
0272 
0273 void OpenVpnSettingWidget::updateStartDir(const QUrl &url)
0274 {
0275     QList<KUrlRequester *> requesters;
0276     requesters << d->ui.x509CaFile << d->ui.x509Cert << d->ui.x509Key << d->ui.pskSharedKey << d->ui.passCaFile << d->ui.x509PassCaFile << d->ui.x509PassCert
0277                << d->ui.x509PassKey;
0278     for (KUrlRequester *requester : std::as_const(requesters)) {
0279         requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
0280     }
0281 }
0282 
0283 void OpenVpnSettingWidget::setPasswordType(QLineEdit *edit, int type)
0284 {
0285     edit->setEnabled(type == SettingWidget::EnumPasswordStorageType::Store);
0286 }
0287 
0288 void OpenVpnSettingWidget::fillOnePasswordCombo(PasswordField *passwordField, NetworkManager::Setting::SecretFlags type)
0289 {
0290     if (type.testFlag(NetworkManager::Setting::None)) {
0291         passwordField->setPasswordOption(PasswordField::StoreForAllUsers);
0292     } else if (type.testFlag(NetworkManager::Setting::AgentOwned)) {
0293         passwordField->setPasswordOption(PasswordField::StoreForUser);
0294     } else if (type.testFlag(NetworkManager::Setting::NotSaved)) {
0295         passwordField->setPasswordOption(PasswordField::AlwaysAsk);
0296     } else if (type.testFlag(NetworkManager::Setting::NotRequired)) {
0297         passwordField->setPasswordOption(PasswordField::NotRequired);
0298     }
0299 }
0300 
0301 void OpenVpnSettingWidget::handleOnePasswordType(const PasswordField *passwordField, const QString &key, NMStringMap &data) const
0302 {
0303     const PasswordField::PasswordOption option = passwordField->passwordOption();
0304     switch (option) {
0305     case PasswordField::StoreForAllUsers:
0306         data.insert(key, QString::number(NetworkManager::Setting::None));
0307         break;
0308     case PasswordField::StoreForUser:
0309         data.insert(key, QString::number(NetworkManager::Setting::AgentOwned));
0310         break;
0311     case PasswordField::AlwaysAsk:
0312         data.insert(key, QString::number(NetworkManager::Setting::NotSaved));
0313         break;
0314     case PasswordField::NotRequired:
0315         data.insert(key, QString::number(NetworkManager::Setting::NotRequired));
0316         break;
0317     }
0318 }
0319 
0320 void OpenVpnSettingWidget::showAdvanced()
0321 {
0322     QPointer<OpenVpnAdvancedWidget> adv = new OpenVpnAdvancedWidget(d->setting, this);
0323     adv->setAttribute(Qt::WA_DeleteOnClose);
0324     adv->init();
0325     connect(adv.data(), &OpenVpnAdvancedWidget::accepted, [adv, this]() {
0326         NetworkManager::VpnSetting::Ptr advData = adv->setting();
0327         if (!advData.isNull()) {
0328             d->setting->setData(advData->data());
0329             d->setting->setSecrets(advData->secrets());
0330         }
0331     });
0332     adv->setModal(true);
0333     adv->show();
0334 }
0335 
0336 bool OpenVpnSettingWidget::isValid() const
0337 {
0338     return !d->ui.gateway->text().isEmpty();
0339 }