File indexing completed on 2024-05-05 17:42:50

0001 /*
0002     SPDX-FileCopyrightText: 2013 Jan Grulich <jgrulich@redhat.com>
0003     SPDX-FileCopyrightText: 2020 Douglas Kosovic <doug@uq.edu.au>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "l2tpipsecwidget.h"
0009 #include "nm-l2tp-service.h"
0010 #include "ui_l2tpipsec.h"
0011 #include <QProcess>
0012 #include <QStandardPaths>
0013 
0014 #include <KAcceleratorManager>
0015 #include <KLocalizedString>
0016 
0017 #define DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS 3
0018 #define DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS 1
0019 
0020 #define DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS 1
0021 #define DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS 8
0022 
0023 L2tpIpsecWidget::L2tpIpsecWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent)
0024     : QDialog(parent)
0025     , m_ui(new Ui::L2tpIpsecWidget)
0026 {
0027     m_ui->setupUi(this);
0028     m_ui->machineKeyPassword->setPasswordOptionsEnabled(true);
0029     m_ui->machineKeyPassword->setPasswordNotRequiredEnabled(true);
0030 
0031     // use requesters' urlSelected signals to set other requester's startDirs to save clicking
0032     // around the filesystem, also if it is a .p12 file,  set the other URLs to that .p12 file.
0033     QList<const KUrlRequester *> requesters;
0034     requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey;
0035     for (const KUrlRequester *requester : requesters) {
0036         connect(requester, &KUrlRequester::urlSelected, this, &L2tpIpsecWidget::updateStartDirUrl);
0037     }
0038 
0039     connect(m_ui->cbIkelifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultIkelifetime);
0040     connect(m_ui->cbSalifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultSalifetime);
0041     connect(m_ui->cmbAuthType, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &L2tpIpsecWidget::resizeStackedWidget);
0042 
0043     setWindowTitle(i18n("L2TP IPsec Options"));
0044 
0045     KAcceleratorManager::manage(this);
0046 
0047     loadConfig(setting);
0048 
0049     resizeStackedWidget(m_ui->cmbAuthType->currentIndex());
0050 }
0051 
0052 L2tpIpsecWidget::~L2tpIpsecWidget()
0053 {
0054     delete m_ui;
0055 }
0056 
0057 void L2tpIpsecWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting)
0058 {
0059     const QString yesString = QLatin1String("yes");
0060     const QString noString = QLatin1String("no");
0061 
0062     // General settings
0063     const NMStringMap dataMap = setting->data();
0064 
0065     if (!hasIpsecDaemon()) {
0066         m_ui->gbEnableTunnelToHost->setChecked(false);
0067         m_ui->gbEnableTunnelToHost->setDisabled(true);
0068     } else if (dataMap[NM_L2TP_KEY_IPSEC_ENABLE] == yesString) {
0069         m_ui->gbEnableTunnelToHost->setChecked(true);
0070 
0071         if (dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE].isEmpty() || dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE] == QLatin1String(NM_L2TP_AUTHTYPE_PSK)) {
0072             m_ui->cmbAuthType->setCurrentIndex(AuthType::PSK);
0073             m_ui->stackedWidget->setCurrentIndex(AuthType::PSK);
0074 
0075             // *SWAN support Base64 encoded PSKs that are prefixed with "0s"
0076             QString psk = dataMap[NM_L2TP_KEY_IPSEC_PSK];
0077             if (psk.length() > 2 && psk.startsWith(QLatin1String("0s"))) {
0078                 m_ui->presharedKey->setText(QByteArray::fromBase64(psk.mid(2).toUtf8()));
0079             } else {
0080                 m_ui->presharedKey->setText(psk);
0081             }
0082 
0083         } else { // NM_L2TP_AUTHTYPE_TLS
0084             m_ui->cmbAuthType->setCurrentIndex(AuthType::TLS);
0085             m_ui->stackedWidget->setCurrentIndex(AuthType::TLS);
0086 
0087             m_ui->machineCA->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CA]));
0088             m_ui->machineCert->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CERT]));
0089             m_ui->machineKey->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_KEY]));
0090 
0091             const NetworkManager::Setting::SecretFlags machineKeyPassType =
0092                 static_cast<NetworkManager::Setting::SecretFlags>(dataMap[NM_L2TP_KEY_MACHINE_CERTPASS "-flags"].toInt());
0093             if (machineKeyPassType.testFlag(NetworkManager::Setting::None)) {
0094                 m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForAllUsers);
0095             } else if (machineKeyPassType.testFlag(NetworkManager::Setting::AgentOwned)) {
0096                 m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForUser);
0097             } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotSaved)) {
0098                 m_ui->machineKeyPassword->setPasswordOption(PasswordField::AlwaysAsk);
0099             } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotRequired)) {
0100                 m_ui->machineKeyPassword->setPasswordOption(PasswordField::NotRequired);
0101             }
0102         }
0103 
0104         if (!dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID].isEmpty()) {
0105             m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID]);
0106         } else {
0107             m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_REMOTE_ID]);
0108         }
0109         m_ui->ike->setText(dataMap[NM_L2TP_KEY_IPSEC_IKE]);
0110         m_ui->esp->setText(dataMap[NM_L2TP_KEY_IPSEC_ESP]);
0111 
0112         if (!dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].isEmpty()) {
0113             const int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].toInt();
0114             const int hours = totalSeconds / 3600;
0115             const int minutes = (totalSeconds % 3600) / 60;
0116             const int seconds = totalSeconds % 60;
0117             const QTime lifetime = QTime(hours, minutes, seconds);
0118 
0119             m_ui->ikelifetime->setTime(lifetime);
0120             m_ui->ikelifetime->setEnabled(true);
0121             m_ui->cbIkelifetime->setChecked(true);
0122         } else {
0123             setDefaultIkelifetime(false);
0124             m_ui->ikelifetime->setEnabled(false);
0125             m_ui->cbIkelifetime->setChecked(false);
0126         }
0127 
0128         if (!dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].isEmpty()) {
0129             const int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].toInt();
0130             const int hours = totalSeconds / 3600;
0131             const int minutes = (totalSeconds % 3600) / 60;
0132             const int seconds = totalSeconds % 60;
0133             const QTime lifetime = QTime(hours, minutes, seconds);
0134 
0135             m_ui->salifetime->setTime(lifetime);
0136             m_ui->salifetime->setEnabled(true);
0137             m_ui->cbSalifetime->setChecked(true);
0138         } else {
0139             setDefaultSalifetime(false);
0140             m_ui->salifetime->setEnabled(false);
0141             m_ui->cbSalifetime->setChecked(false);
0142         }
0143 
0144         m_ui->cbForceEncaps->setChecked(dataMap[NM_L2TP_KEY_IPSEC_FORCEENCAPS] == yesString);
0145         m_ui->cbIPComp->setChecked(dataMap[NM_L2TP_KEY_IPSEC_IPCOMP] == yesString);
0146 
0147         if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) {
0148             m_ui->cbPFS->setChecked(dataMap[NM_L2TP_KEY_IPSEC_PFS] == noString);
0149         } else {
0150             m_ui->cbPFS->setChecked(false);
0151             m_ui->cbPFS->setDisabled(true);
0152             m_ui->cbPFS->setToolTip("");
0153         }
0154     } else {
0155         m_ui->gbEnableTunnelToHost->setChecked(false);
0156         setDefaultIkelifetime(false);
0157         setDefaultSalifetime(false);
0158         if (m_ipsecDaemonType == IpsecDaemonType::Strongswan) {
0159             m_ui->cbPFS->setDisabled(true);
0160             m_ui->cbPFS->setToolTip("");
0161         }
0162     }
0163 }
0164 
0165 NMStringMap L2tpIpsecWidget::setting() const
0166 {
0167     NMStringMap result;
0168     const QString yesString = QLatin1String("yes");
0169     const QString noString = QLatin1String("no");
0170 
0171     if (m_ui->gbEnableTunnelToHost->isChecked()) {
0172         result.insert(NM_L2TP_KEY_IPSEC_ENABLE, yesString);
0173 
0174         if (m_ui->cmbAuthType->currentIndex() == AuthType::PSK) {
0175             // NetworkManager-l2tp < 1.2.12 does not support Base64 PSK
0176             // For backwards compatibility don't use Base64 PSK for now.
0177             if (!m_ui->presharedKey->text().isEmpty()) {
0178                 result.insert(NM_L2TP_KEY_IPSEC_PSK, m_ui->presharedKey->text());
0179                 // *SWAN support Base64 encoded PSKs that are prefixed with "0s"
0180                 /*
0181                 QString psk = QLatin1String("0s");
0182                 psk.append(m_ui->presharedKey->text().toUtf8().toBase64());
0183                 result.insert(NM_L2TP_KEY_IPSEC_PSK, psk);
0184                 */
0185             }
0186         } else { // AuthType::TLS
0187 
0188             result.insert(NM_L2TP_KEY_MACHINE_AUTH_TYPE, NM_L2TP_AUTHTYPE_TLS);
0189 
0190             result.insert(NM_L2TP_KEY_MACHINE_CA, m_ui->machineCA->url().toLocalFile());
0191             result.insert(NM_L2TP_KEY_MACHINE_CERT, m_ui->machineCert->url().toLocalFile());
0192             result.insert(NM_L2TP_KEY_MACHINE_KEY, m_ui->machineKey->url().toLocalFile());
0193 
0194             switch (m_ui->machineKeyPassword->passwordOption()) {
0195             case PasswordField::StoreForAllUsers:
0196                 result.insert(NM_L2TP_KEY_MACHINE_CERTPASS "-flags", QString::number(NetworkManager::Setting::None));
0197                 break;
0198             case PasswordField::StoreForUser:
0199                 result.insert(NM_L2TP_KEY_MACHINE_CERTPASS "-flags", QString::number(NetworkManager::Setting::AgentOwned));
0200                 break;
0201             case PasswordField::AlwaysAsk:
0202                 result.insert(NM_L2TP_KEY_MACHINE_CERTPASS "-flags", QString::number(NetworkManager::Setting::NotSaved));
0203                 break;
0204             case PasswordField::NotRequired:
0205                 result.insert(NM_L2TP_KEY_MACHINE_CERTPASS "-flags", QString::number(NetworkManager::Setting::NotRequired));
0206                 break;
0207             };
0208         }
0209 
0210         // NetworkManager-l2tp 1.2.12 recommends NM_L2TP_KEY_IPSEC_REMOTE_ID
0211         // instead of deprecated NM_L2TP_KEY_IPSEC_GATEWAY_ID
0212         // For backwards compatibility use NM_L2TP_KEY_IPSEC_GATEWAY_ID for now.
0213         if (!m_ui->remoteId->text().isEmpty()) {
0214             result.insert(NM_L2TP_KEY_IPSEC_GATEWAY_ID, m_ui->remoteId->text());
0215         }
0216 
0217         if (!m_ui->ike->text().isEmpty()) {
0218             result.insert(NM_L2TP_KEY_IPSEC_IKE, m_ui->ike->text());
0219         }
0220 
0221         if (!m_ui->esp->text().isEmpty()) {
0222             result.insert(NM_L2TP_KEY_IPSEC_ESP, m_ui->esp->text());
0223         }
0224 
0225         if (m_ui->cbForceEncaps->isChecked()) {
0226             result.insert(NM_L2TP_KEY_IPSEC_FORCEENCAPS, yesString);
0227         }
0228 
0229         if (m_ui->cbIkelifetime->isChecked()) {
0230             const int totalSeconds = m_ui->ikelifetime->time().hour() * 3600 + m_ui->ikelifetime->time().minute() * 60 + m_ui->ikelifetime->time().second();
0231 
0232             result.insert(NM_L2TP_KEY_IPSEC_IKELIFETIME, QString::number(totalSeconds));
0233         }
0234 
0235         if (m_ui->cbSalifetime->isChecked()) {
0236             const int totalSeconds = m_ui->salifetime->time().hour() * 3600 + m_ui->salifetime->time().minute() * 60 + m_ui->salifetime->time().second();
0237 
0238             result.insert(NM_L2TP_KEY_IPSEC_SALIFETIME, QString::number(totalSeconds));
0239         }
0240 
0241         if (m_ui->cbIPComp->isChecked()) {
0242             result.insert(NM_L2TP_KEY_IPSEC_IPCOMP, yesString);
0243         }
0244 
0245         if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) {
0246             if (m_ui->cbPFS->isChecked()) {
0247                 result.insert(NM_L2TP_KEY_IPSEC_PFS, noString);
0248             }
0249         }
0250     }
0251 
0252     return result;
0253 }
0254 
0255 NMStringMap L2tpIpsecWidget::secrets() const
0256 {
0257     NMStringMap result;
0258 
0259     if (m_ui->gbEnableTunnelToHost->isChecked()) {
0260         if (m_ui->cmbAuthType->currentIndex() == AuthType::TLS) {
0261             // private key password
0262             if (!m_ui->machineKeyPassword->text().isEmpty()) {
0263                 result.insert(NM_L2TP_KEY_MACHINE_CERTPASS, m_ui->machineKeyPassword->text());
0264             }
0265         }
0266     }
0267 
0268     return result;
0269 }
0270 
0271 void L2tpIpsecWidget::updateStartDirUrl(const QUrl &url)
0272 {
0273     QList<KUrlRequester *> requesters;
0274     requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey;
0275     const bool isP12 = url.toString().endsWith(QLatin1String(".p12"));
0276 
0277     for (KUrlRequester *requester : requesters) {
0278         requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash));
0279         if (isP12) {
0280             requester->setUrl(url);
0281         }
0282     }
0283 }
0284 
0285 void L2tpIpsecWidget::setDefaultIkelifetime(bool isChecked)
0286 {
0287     if (!isChecked) {
0288         QTime lifetime;
0289         if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) {
0290             lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS, 0, 0);
0291         } else {
0292             lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS, 0, 0);
0293         }
0294         m_ui->ikelifetime->setTime(lifetime);
0295     }
0296 }
0297 
0298 void L2tpIpsecWidget::setDefaultSalifetime(bool isChecked)
0299 {
0300     if (!isChecked) {
0301         QTime lifetime;
0302         if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) {
0303             lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS, 0, 0);
0304         } else {
0305             lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS, 0, 0);
0306         }
0307         m_ui->salifetime->setTime(lifetime);
0308     }
0309 }
0310 
0311 void L2tpIpsecWidget::resizeStackedWidget(int currentIndex)
0312 {
0313     m_ui->stackedWidget->setCurrentIndex(currentIndex);
0314     for (int i = 0; i < m_ui->stackedWidget->count(); ++i) {
0315         QSizePolicy::Policy policy;
0316 
0317         if (i == currentIndex) {
0318             policy = QSizePolicy::Preferred;
0319         } else {
0320             policy = QSizePolicy::Ignored;
0321         }
0322         m_ui->stackedWidget->widget(i)->setSizePolicy(QSizePolicy::Preferred, policy);
0323     }
0324     QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
0325     resize(width(), sizeHint().height());
0326 }
0327 
0328 L2tpIpsecWidget::IpsecDaemonType L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon;
0329 
0330 bool L2tpIpsecWidget::hasIpsecDaemon()
0331 {
0332     // NetworkManager-l2tp currently only supports libreswan and strongswan
0333     if (m_ipsecDaemonType == IpsecDaemonType::Libreswan || m_ipsecDaemonType == IpsecDaemonType::Strongswan) {
0334         return true;
0335     }
0336 
0337     QString ipsecBinary = QStandardPaths::findExecutable(QStringLiteral("ipsec"), QStringList() << QStringLiteral("/sbin") << QStringLiteral("/usr/sbin"));
0338 
0339     // On some Linux distributions, ipsec executable has been renamed strongswan
0340     if (ipsecBinary.isEmpty()) {
0341         ipsecBinary = QStandardPaths::findExecutable(QStringLiteral("strongswan"), QStringList() << QStringLiteral("/sbin") << QStringLiteral("/usr/sbin"));
0342     }
0343 
0344     if (ipsecBinary.isEmpty()) {
0345         m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon;
0346         return false;
0347     }
0348 
0349     QProcess ipsecVersionProcess;
0350     ipsecVersionProcess.setProgram(ipsecBinary);
0351     ipsecVersionProcess.setArguments(QStringList() << QStringLiteral("--version"));
0352     ipsecVersionProcess.start();
0353     ipsecVersionProcess.waitForFinished(-1);
0354 
0355     if (ipsecVersionProcess.exitStatus() == QProcess::NormalExit) {
0356         QString ipsecStdout = ipsecVersionProcess.readAllStandardOutput();
0357 
0358         if (ipsecStdout.contains("strongSwan", Qt::CaseSensitive)) {
0359             L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Strongswan;
0360         } else if (ipsecStdout.contains("Libreswan", Qt::CaseSensitive)) {
0361             L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Libreswan;
0362         } else if (ipsecStdout.contains("Openswan", Qt::CaseSensitive)) {
0363             L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Openswan;
0364         } else {
0365             L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::UnknownIpsecDaemon;
0366         }
0367     }
0368 
0369     if (m_ipsecDaemonType == IpsecDaemonType::Libreswan || m_ipsecDaemonType == IpsecDaemonType::Strongswan) {
0370         return true;
0371     }
0372     return false;
0373 }