File indexing completed on 2024-04-28 16:52:52

0001 /*
0002     SPDX-FileCopyrightText: 2015 Jan Grulich <jgrulich@redhat.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "sshwidget.h"
0008 
0009 #include "ui_sshadvanced.h"
0010 #include "ui_sshwidget.h"
0011 
0012 #include "simpleipv4addressvalidator.h"
0013 #include "simpleipv6addressvalidator.h"
0014 
0015 #include <QDialog>
0016 #include <QDialogButtonBox>
0017 #include <QString>
0018 
0019 #include "nm-ssh-service.h"
0020 
0021 class SshSettingWidgetPrivate
0022 {
0023 public:
0024     Ui_SshWidget ui;
0025     Ui_SshAdvanced advUi;
0026     NetworkManager::VpnSetting::Ptr setting;
0027     QDialog *advancedDlg = nullptr;
0028     QWidget *advancedWid = nullptr;
0029 };
0030 
0031 SshSettingWidget::SshSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent)
0032     : SettingWidget(setting, parent)
0033     , d_ptr(new SshSettingWidgetPrivate)
0034 {
0035     Q_D(SshSettingWidget);
0036     d->ui.setupUi(this);
0037 
0038     d->setting = setting;
0039 
0040     d->ui.le_password->setPasswordOptionsEnabled(true);
0041 
0042     connect(d->ui.cmb_authType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SshSettingWidget::authTypeChanged);
0043     connect(d->ui.btn_advancedOption, &QPushButton::clicked, this, &SshSettingWidget::doAdvancedDialog);
0044 
0045     d->advancedDlg = new QDialog(this);
0046     d->advancedDlg->setModal(true);
0047     d->advancedWid = new QWidget(this);
0048     d->advUi.setupUi(d->advancedWid);
0049     auto layout = new QVBoxLayout(d->advancedDlg);
0050     layout->addWidget(d->advancedWid);
0051     d->advancedDlg->setLayout(layout);
0052     auto buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, d->advancedDlg);
0053     connect(buttons, &QDialogButtonBox::accepted, d->advancedDlg, &QDialog::accept);
0054     connect(buttons, &QDialogButtonBox::rejected, d->advancedDlg, &QDialog::reject);
0055 
0056     layout->addWidget(buttons);
0057 
0058     // Connect for setting check
0059     watchChangedSetting();
0060 
0061     // Connect for validity check
0062     connect(d->ui.le_gateway, &QLineEdit::textChanged, this, &SshSettingWidget::slotWidgetChanged);
0063     connect(d->ui.le_localIp, &QLineEdit::textChanged, this, &SshSettingWidget::slotWidgetChanged);
0064     connect(d->ui.le_netmask, &QLineEdit::textChanged, this, &SshSettingWidget::slotWidgetChanged);
0065     connect(d->ui.le_remoteIp, &QLineEdit::textChanged, this, &SshSettingWidget::slotWidgetChanged);
0066 
0067     auto ipv4Validator = new SimpleIpV4AddressValidator(SimpleIpV4AddressValidator::Base, this);
0068     d->ui.le_localIp->setValidator(ipv4Validator);
0069     d->ui.le_remoteIp->setValidator(ipv4Validator);
0070     d->ui.le_netmask->setValidator(ipv4Validator);
0071 
0072     auto ipv6Validator = new SimpleIpV6AddressValidator(SimpleIpV6AddressValidator::Base, this);
0073     d->ui.le_localIpv6->setValidator(ipv6Validator);
0074     d->ui.le_remoteIpv6->setValidator(ipv6Validator);
0075 
0076     d->ui.passwordWidget->setVisible(false);
0077     d->advUi.sb_useCustomGatewayPort->setValue(NM_SSH_DEFAULT_PORT);
0078     d->advUi.sb_useCustomTunnelMtu->setValue(NM_SSH_DEFAULT_MTU);
0079     d->advUi.le_extraSshOptions->setText(QLatin1String(NM_SSH_DEFAULT_EXTRA_OPTS));
0080     d->advUi.sb_remoteDeviceNumber->setValue(NM_SSH_DEFAULT_REMOTE_DEV);
0081     d->advUi.le_remoteUsername->setText(QLatin1String(NM_SSH_DEFAULT_REMOTE_USERNAME));
0082 
0083     KAcceleratorManager::manage(this);
0084 
0085     if (d->setting && !d->setting->isNull()) {
0086         loadConfig(d->setting);
0087     }
0088 }
0089 
0090 SshSettingWidget::~SshSettingWidget()
0091 {
0092     delete d_ptr;
0093 }
0094 
0095 void SshSettingWidget::loadConfig(const NetworkManager::Setting::Ptr &setting)
0096 {
0097     Q_D(SshSettingWidget);
0098     Q_UNUSED(setting)
0099 
0100     const NMStringMap dataMap = d->setting->data();
0101 
0102     // General
0103     const QString gateway = dataMap[QLatin1String(NM_SSH_KEY_REMOTE)];
0104     if (!gateway.isEmpty()) {
0105         d->ui.le_gateway->setText(gateway);
0106     }
0107 
0108     // Network settings
0109     const QString remoteIp = dataMap[QLatin1String(NM_SSH_KEY_REMOTE_IP)];
0110     if (!remoteIp.isEmpty()) {
0111         d->ui.le_remoteIp->setText(remoteIp);
0112     }
0113 
0114     const QString localIp = dataMap[QLatin1String(NM_SSH_KEY_LOCAL_IP)];
0115     if (!localIp.isEmpty()) {
0116         d->ui.le_localIp->setText(localIp);
0117     }
0118 
0119     const QString netmask = dataMap[QLatin1String(NM_SSH_KEY_NETMASK)];
0120     if (!netmask.isEmpty()) {
0121         d->ui.le_netmask->setText(netmask);
0122     }
0123 
0124     // IPv6 network settings
0125     const bool ipv6Enabled = dataMap[QLatin1String(NM_SSH_KEY_IP_6)] == QLatin1String("yes");
0126     d->ui.chk_useIpv6->setChecked(ipv6Enabled);
0127 
0128     if (ipv6Enabled) {
0129         const QString remoteIpv6 = dataMap[QLatin1String(NM_SSH_KEY_REMOTE_IP_6)];
0130         if (!remoteIpv6.isEmpty()) {
0131             d->ui.le_remoteIpv6->setText(remoteIpv6);
0132         }
0133 
0134         const QString localIpv6 = dataMap[QLatin1String(NM_SSH_KEY_LOCAL_IP_6)];
0135         if (!localIpv6.isEmpty()) {
0136             d->ui.le_localIpv6->setText(localIpv6);
0137         }
0138 
0139         const QString netmaskIpv6 = dataMap[QLatin1String(NM_SSH_KEY_NETMASK_6)];
0140         if (!netmaskIpv6.isEmpty()) {
0141             d->ui.le_netmaskIpv6->setText(netmaskIpv6);
0142         }
0143     }
0144 
0145     // Authentication
0146     const QString sshAuthType = dataMap[QLatin1String(NM_SSH_KEY_AUTH_TYPE)];
0147     if (sshAuthType == QLatin1String(NM_SSH_AUTH_TYPE_SSH_AGENT)) {
0148         d->ui.cmb_authType->setCurrentIndex(0);
0149     } else if (sshAuthType == QLatin1String(NM_SSH_AUTH_TYPE_PASSWORD)) {
0150         d->ui.cmb_authType->setCurrentIndex(1);
0151         const NetworkManager::Setting::SecretFlags type = (NetworkManager::Setting::SecretFlags)dataMap[NM_SSH_KEY_PASSWORD "-flags"].toInt();
0152         fillOnePasswordCombo(d->ui.le_password, type);
0153     } else if (sshAuthType == QLatin1String(NM_SSH_AUTH_TYPE_KEY)) {
0154         d->ui.cmb_authType->setCurrentIndex(2);
0155         d->ui.kurl_sshKeyFile->setUrl(QUrl::fromLocalFile(dataMap[QLatin1String(NM_SSH_KEY_KEY_FILE)]));
0156     }
0157 
0158     // Options below is belongs to "Advanced" dialog
0159     const QString customGatewayPort = dataMap[QLatin1String(NM_SSH_KEY_PORT)];
0160     if (!customGatewayPort.isEmpty()) {
0161         d->advUi.chk_useCustomGatewayPort->setChecked(true);
0162         d->advUi.sb_useCustomGatewayPort->setValue(customGatewayPort.toInt());
0163     }
0164 
0165     const QString customMtu = dataMap[QLatin1String(NM_SSH_KEY_TUNNEL_MTU)];
0166     if (!customMtu.isEmpty()) {
0167         d->advUi.chk_useCustomTunnelMtu->setChecked(true);
0168         d->advUi.sb_useCustomTunnelMtu->setValue(customMtu.toInt());
0169     }
0170 
0171     const QString extraSshOptions = dataMap[QLatin1String(NM_SSH_KEY_EXTRA_OPTS)];
0172     if (!extraSshOptions.isEmpty()) {
0173         d->advUi.chk_extraSshOptions->setChecked(true);
0174         d->advUi.le_extraSshOptions->setText(extraSshOptions);
0175     }
0176 
0177     const QString remoteDeviceNumber = dataMap[QLatin1String(NM_SSH_KEY_REMOTE_DEV)];
0178     if (!remoteDeviceNumber.isEmpty()) {
0179         d->advUi.chk_remoteDeviceNumber->setChecked(true);
0180         d->advUi.sb_remoteDeviceNumber->setValue(remoteDeviceNumber.toInt());
0181     }
0182 
0183     const QString useTapDevice = dataMap[QLatin1String(NM_SSH_KEY_TAP_DEV)];
0184     if (!useTapDevice.isEmpty()) {
0185         if (useTapDevice == QLatin1String("yes")) {
0186             d->advUi.chk_useTapDevice->setChecked(true);
0187         }
0188     }
0189 
0190     const QString remoteUsername = dataMap[QLatin1String(NM_SSH_KEY_REMOTE_USERNAME)];
0191     if (!remoteUsername.isEmpty()) {
0192         d->advUi.chk_remoteUsername->setChecked(true);
0193         d->advUi.le_remoteUsername->setText(remoteUsername);
0194     }
0195 
0196     const QString doNotReplaceDefaultRoute = dataMap[QLatin1String(NM_SSH_KEY_NO_DEFAULT_ROUTE)];
0197     if (!doNotReplaceDefaultRoute.isEmpty()) {
0198         if (doNotReplaceDefaultRoute == QLatin1String("yes")) {
0199             d->advUi.chk_doNotReplaceDefaultRoute->setChecked(true);
0200         }
0201     }
0202 
0203     loadSecrets(setting);
0204 }
0205 
0206 void SshSettingWidget::loadSecrets(const NetworkManager::Setting::Ptr &setting)
0207 {
0208     Q_D(SshSettingWidget);
0209 
0210     NetworkManager::VpnSetting::Ptr vpnSetting = setting.staticCast<NetworkManager::VpnSetting>();
0211 
0212     if (vpnSetting) {
0213         const NMStringMap secrets = vpnSetting->secrets();
0214         const QString keyPassword = secrets.value(NM_SSH_KEY_PASSWORD);
0215         if (!keyPassword.isEmpty()) {
0216             d->ui.le_password->setText(keyPassword);
0217         }
0218     }
0219 }
0220 
0221 QVariantMap SshSettingWidget::setting() const
0222 {
0223     Q_D(const SshSettingWidget);
0224 
0225     NetworkManager::VpnSetting setting;
0226     setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_SSH));
0227 
0228     NMStringMap data;
0229     NMStringMap secretData;
0230 
0231     data.insert(QLatin1String(NM_SSH_KEY_REMOTE), d->ui.le_gateway->text());
0232 
0233     if (!d->ui.le_remoteIp->text().isEmpty()) {
0234         data.insert(QLatin1String(NM_SSH_KEY_REMOTE_IP), d->ui.le_remoteIp->text());
0235     }
0236 
0237     if (!d->ui.le_localIp->text().isEmpty()) {
0238         data.insert(QLatin1String(NM_SSH_KEY_LOCAL_IP), d->ui.le_localIp->text());
0239     }
0240 
0241     if (!d->ui.le_netmask->text().isEmpty()) {
0242         data.insert(QLatin1String(NM_SSH_KEY_NETMASK), d->ui.le_netmask->text());
0243     }
0244 
0245     if (d->ui.chk_useIpv6->isChecked()) {
0246         data.insert(QLatin1String(NM_SSH_KEY_IP_6), QLatin1String("yes"));
0247 
0248         if (!d->ui.le_remoteIpv6->text().isEmpty()) {
0249             data.insert(QLatin1String(NM_SSH_KEY_REMOTE_IP_6), d->ui.le_remoteIpv6->text());
0250         }
0251 
0252         if (!d->ui.le_localIpv6->text().isEmpty()) {
0253             data.insert(QLatin1String(NM_SSH_KEY_LOCAL_IP_6), d->ui.le_localIpv6->text());
0254         }
0255 
0256         if (!d->ui.le_netmaskIpv6->text().isEmpty()) {
0257             data.insert(QLatin1String(NM_SSH_KEY_NETMASK_6), d->ui.le_netmaskIpv6->text());
0258         }
0259     }
0260 
0261     switch (d->ui.cmb_authType->currentIndex()) {
0262     case 0:
0263         data.insert(QLatin1String(NM_SSH_KEY_AUTH_TYPE), QLatin1String(NM_SSH_AUTH_TYPE_SSH_AGENT));
0264         break;
0265     case 1:
0266         data.insert(QLatin1String(NM_SSH_KEY_AUTH_TYPE), QLatin1String(NM_SSH_AUTH_TYPE_PASSWORD));
0267         if (!d->ui.le_password->text().isEmpty()) {
0268             secretData.insert(QLatin1String(NM_SSH_KEY_PASSWORD), d->ui.le_password->text());
0269         }
0270         handleOnePasswordType(d->ui.le_password, NM_SSH_KEY_PASSWORD "-flags", data);
0271         break;
0272     case 2:
0273         data.insert(QLatin1String(NM_SSH_KEY_AUTH_TYPE), QLatin1String(NM_SSH_AUTH_TYPE_KEY));
0274         if (!d->ui.kurl_sshKeyFile->url().isEmpty()) {
0275             data.insert(QLatin1String(NM_SSH_KEY_KEY_FILE), d->ui.kurl_sshKeyFile->url().toLocalFile());
0276         }
0277         break;
0278     }
0279 
0280     if (d->advUi.chk_useCustomGatewayPort->isChecked()) {
0281         data.insert(QLatin1String(NM_SSH_KEY_PORT), QString::number(d->advUi.sb_useCustomGatewayPort->value()));
0282     }
0283 
0284     if (d->advUi.chk_useCustomTunnelMtu->isChecked()) {
0285         data.insert(QLatin1String(NM_SSH_KEY_TUNNEL_MTU), QString::number(d->advUi.sb_useCustomTunnelMtu->value()));
0286     }
0287 
0288     if (d->advUi.chk_extraSshOptions->isChecked()) {
0289         data.insert(QLatin1String(NM_SSH_KEY_EXTRA_OPTS), d->advUi.le_extraSshOptions->text());
0290     }
0291 
0292     if (d->advUi.chk_remoteDeviceNumber->isChecked()) {
0293         data.insert(QLatin1String(NM_SSH_KEY_REMOTE_DEV), QString::number(d->advUi.sb_remoteDeviceNumber->value()));
0294     }
0295 
0296     if (d->advUi.chk_useTapDevice->isChecked()) {
0297         data.insert(QLatin1String(NM_SSH_KEY_TAP_DEV), QLatin1String("yes"));
0298     }
0299 
0300     if (d->advUi.chk_remoteUsername->isChecked()) {
0301         data.insert(QLatin1String(NM_SSH_KEY_REMOTE_USERNAME), d->advUi.le_remoteUsername->text());
0302     }
0303 
0304     if (d->advUi.chk_doNotReplaceDefaultRoute->isChecked()) {
0305         data.insert(QLatin1String(NM_SSH_KEY_NO_DEFAULT_ROUTE), QLatin1String("yes"));
0306     }
0307 
0308     // save it all
0309     setting.setData(data);
0310     setting.setSecrets(secretData);
0311 
0312     return setting.toMap();
0313 }
0314 
0315 void SshSettingWidget::authTypeChanged(int index)
0316 {
0317     Q_D(SshSettingWidget);
0318 
0319     if (index == 0) {
0320         d->ui.stackedWidget->setCurrentIndex(0);
0321         d->ui.passwordWidget->setVisible(false);
0322     } else if (index == 1) {
0323         d->ui.stackedWidget->setCurrentIndex(0);
0324         d->ui.passwordWidget->setVisible(true);
0325     } else {
0326         d->ui.stackedWidget->setCurrentIndex(1);
0327     }
0328 }
0329 
0330 void SshSettingWidget::doAdvancedDialog()
0331 {
0332     Q_D(SshSettingWidget);
0333     d->advancedDlg->show();
0334 }
0335 
0336 void SshSettingWidget::passwordTypeChanged(int index)
0337 {
0338     Q_D(SshSettingWidget);
0339     d->ui.le_password->setEnabled(index == SettingWidget::EnumPasswordStorageType::Store);
0340 }
0341 
0342 void SshSettingWidget::fillOnePasswordCombo(PasswordField *passwordField, NetworkManager::Setting::SecretFlags type)
0343 {
0344     if (type.testFlag(NetworkManager::Setting::None)) {
0345         passwordField->setPasswordOption(PasswordField::StoreForAllUsers);
0346     } else if (type.testFlag(NetworkManager::Setting::AgentOwned)) {
0347         passwordField->setPasswordOption(PasswordField::StoreForUser);
0348     } else if (type.testFlag(NetworkManager::Setting::NotSaved)) {
0349         passwordField->setPasswordOption(PasswordField::AlwaysAsk);
0350     } else {
0351         passwordField->setPasswordOption(PasswordField::PasswordField::NotRequired);
0352     }
0353 }
0354 
0355 void SshSettingWidget::handleOnePasswordType(const PasswordField *passwordField, const QString &key, NMStringMap &data) const
0356 {
0357     const PasswordField::PasswordOption option = passwordField->passwordOption();
0358     switch (option) {
0359     case PasswordField::StoreForAllUsers:
0360         data.insert(key, QString::number(NetworkManager::Setting::None));
0361         break;
0362     case PasswordField::StoreForUser:
0363         data.insert(key, QString::number(NetworkManager::Setting::AgentOwned));
0364         break;
0365     case PasswordField::AlwaysAsk:
0366         data.insert(key, QString::number(NetworkManager::Setting::NotSaved));
0367         break;
0368     case PasswordField::NotRequired:
0369         data.insert(key, QString::number(NetworkManager::Setting::NotRequired));
0370         break;
0371     }
0372 }
0373 
0374 bool SshSettingWidget::isValid() const
0375 {
0376     Q_D(const SshSettingWidget);
0377 
0378     return !d->ui.le_gateway->text().isEmpty() && !d->ui.le_localIp->text().isEmpty() && !d->ui.le_remoteIp->text().isEmpty()
0379         && !d->ui.le_netmask->text().isEmpty();
0380 }