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 }