diff --git a/vpn/l2tp/CMakeLists.txt b/vpn/l2tp/CMakeLists.txt --- a/vpn/l2tp/CMakeLists.txt +++ b/vpn/l2tp/CMakeLists.txt @@ -8,7 +8,7 @@ l2tppppwidget.cpp ) -ki18n_wrap_ui(l2tp_SRCS l2tp.ui l2tpipsec.ui l2tpauth.ui l2tpppp.ui) +ki18n_wrap_ui(l2tp_SRCS l2tp.ui l2tpipsec.ui l2tpppp.ui) add_library(plasmanetworkmanagement_l2tpui ${l2tp_SRCS}) diff --git a/vpn/l2tp/l2tp.cpp b/vpn/l2tp/l2tp.cpp --- a/vpn/l2tp/l2tp.cpp +++ b/vpn/l2tp/l2tp.cpp @@ -45,7 +45,7 @@ SettingWidget *L2tpUiPlugin::askUser(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) { - return new L2tpAuthDialog(setting, parent); + return new L2tpAuthWidget(setting, parent); } QString L2tpUiPlugin::suggestedFileName(const NetworkManager::ConnectionSettings::Ptr &connection) const diff --git a/vpn/l2tp/l2tp.ui b/vpn/l2tp/l2tp.ui --- a/vpn/l2tp/l2tp.ui +++ b/vpn/l2tp/l2tp.ui @@ -6,137 +6,267 @@ 0 0 - 450 - 388 + 495 + 422 - - - 6 - - - - - Gateway: - - - - - - - L2TP server IP or name. - - - true - - - - - - - User name: - - - - - - - Set the name used for authenticating the local system to the peer to <name>. - - - true - - - - - - - Password: - - - - - - - + + + + + + + Gateway: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + gateway + + + + + - Password passed to PPPD when prompted for it. + L2TP server IP or name. - + true + + + + Authentication type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + cmbAuthType + + + + + + + + 0 + 0 + + + + Select an authentication mode. + + + + Password + + + + + Certificates (TLS) + + + + - - - - NT Domain: - + + + + + + + + Username: + + + false + + + username + + + + + + + <html><head/><body><p>Set the name used for authenticating the local system to the peer to &lt;name&gt;.</p></body></html> + + + true + + + + + + + Password: + + + password + + + + + + + + + true + + + <html><head/><body><p>Password passed to PPPD when prompted for it.</p></body></html> + + + true + + + + + + + + + NT Domain: + + + domain + + + + + + + <html><head/><body><p>Append the domain name &lt;domain&gt; to the local host name for</p><p>authentication purposes.</p></body></html> + + + + + + + Qt::Vertical + + + + 347 + 200 + + + + + + + + + + + + CA Certificate: + + + false + + + userCA + + + + + + + <html><head/><body><p>Certificate authority (CA) file in .pem, .der, .crt, .crt or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + User Certificate: + + + false + + + userCert + + + + + + + <html><head/><body><p>Certificate in .pem, .der, .crt, .cer or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Private Key: + + + false + + + userKey + + + + + + + <html><head/><body><p>Private key in .pem, .der, .key, .pk8 or .p12 formats.</p></body></html> + + + *.pem *der *.key *.pk8 *.p12 + + + + + + + Private Key Password: + + + userKeyPassword + + + + + + + + + <html><head/><body><p>Password for private key or PKCS#12 certificate.</p></body></html> + + + true + + + + + + + - - - - Append the domain name <domain> to the local host name for -authentication purposes. - - - true - - - - - - - Qt::Horizontal - - - - - - - - - - - - - - CA Certificate: - - - - - - - Certificate: - - - - - - - Private Key: - - - - - - - Qt::Vertical - - - - 20 - 216 - - - - - - + + @@ -144,8 +274,8 @@ - 188 - 22 + 40 + 20 @@ -166,36 +296,9 @@ - - - - false - - - - - - - false - - - - - - - false - - - - - - - Use Certificate - - - + KUrlRequester @@ -210,12 +313,34 @@ gateway + cmbAuthType username password domain + userCA + userCert + userKey + userKeyPassword btnIPSecSettings btnPPPSettings - + + + cmbAuthType + currentIndexChanged(int) + stackedWidget + setCurrentIndex(int) + + + 345 + 76 + + + 488 + 234 + + + + diff --git a/vpn/l2tp/l2tpauth.h b/vpn/l2tp/l2tpauth.h --- a/vpn/l2tp/l2tpauth.h +++ b/vpn/l2tp/l2tpauth.h @@ -1,21 +1,22 @@ /* - Copyright 2013 Jan Grulich - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library is distributed in the hope that it will be useful, + Copyright 2011 Ilia Kats + Copyright 2013 Lukáš Tinkl + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #ifndef PLASMA_NM_L2TP_AUTH_H @@ -25,20 +26,20 @@ #include "settingwidget.h" -class L2tpAuthDialogPrivate; +class L2tpAuthWidgetPrivate; -class L2tpAuthDialog : public SettingWidget +class L2tpAuthWidget : public SettingWidget { Q_OBJECT - Q_DECLARE_PRIVATE(L2tpAuthDialog) + Q_DECLARE_PRIVATE(L2tpAuthWidget) public: - explicit L2tpAuthDialog(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); - ~L2tpAuthDialog() override; + explicit L2tpAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); + ~L2tpAuthWidget() override; virtual void readSecrets(); QVariantMap setting() const override; private: - L2tpAuthDialogPrivate *const d_ptr; + L2tpAuthWidgetPrivate *const d_ptr; }; #endif // PLASMA_NM_L2TP_AUTH_H diff --git a/vpn/l2tp/l2tpauth.cpp b/vpn/l2tp/l2tpauth.cpp --- a/vpn/l2tp/l2tpauth.cpp +++ b/vpn/l2tp/l2tpauth.cpp @@ -1,92 +1,131 @@ /* - Copyright 2013 Jan Grulich - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) version 3, or any - later version accepted by the membership of KDE e.V. (or its - successor approved by the membership of KDE e.V.), which shall - act as a proxy defined in Section 6 of version 3 of the license. - - This library is distributed in the hope that it will be useful, + Copyright 2011 Ilia Kats + Copyright 2013 Lukáš Tinkl + Copyright 2020 Douglas Kosovic + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License or (at your option) version 3 or any later version + accepted by the membership of KDE e.V. (or its successor approved + by the membership of KDE e.V.), which shall act as a proxy + defined in Section 14 of version 3 of the license. + + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library. If not, see . + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ #include "l2tpauth.h" -#include "ui_l2tpauth.h" -#include "nm-l2tp-service.h" +#include "passwordfield.h" #include +#include +#include +#include + +#include -class L2tpAuthDialogPrivate +#include "nm-l2tp-service.h" +#include "debug.h" + +class L2tpAuthWidgetPrivate { public: - Ui_L2tpAuth ui; NetworkManager::VpnSetting::Ptr setting; + QFormLayout *layout; }; -L2tpAuthDialog::L2tpAuthDialog(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) +L2tpAuthWidget::L2tpAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) : SettingWidget(setting, parent) - , d_ptr(new L2tpAuthDialogPrivate) + , d_ptr(new L2tpAuthWidgetPrivate) { - Q_D(L2tpAuthDialog); - d->ui.setupUi(this); + Q_D(L2tpAuthWidget); d->setting = setting; - - KAcceleratorManager::manage(this); + d->layout = new QFormLayout(this); + setLayout(d->layout); readSecrets(); + + KAcceleratorManager::manage(this); } -L2tpAuthDialog::~L2tpAuthDialog() +L2tpAuthWidget::~L2tpAuthWidget() { delete d_ptr; } -void L2tpAuthDialog::readSecrets() +void L2tpAuthWidget::readSecrets() { - Q_D(L2tpAuthDialog); - const NMStringMap data = d->setting->data(); + Q_D(L2tpAuthWidget); const NMStringMap secrets = d->setting->secrets(); - - QString user = data.value(NM_L2TP_KEY_USER); - if (!user.isEmpty()) { - d->ui.leUserName->setText(user); + const NMStringMap dataMap = d->setting->data(); + const QString userAType = dataMap[NM_L2TP_KEY_USER_AUTH_TYPE]; + const QString machineAType = dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE]; + QLabel *label; + PasswordField *lineEdit; + + NetworkManager::Setting::SecretFlags passType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_PASSWORD"-flags"].toInt(); + NetworkManager::Setting::SecretFlags userCertType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_USER_CERTPASS"-flags"].toInt(); + NetworkManager::Setting::SecretFlags machineCertType = (NetworkManager::Setting::SecretFlags)dataMap[NM_L2TP_KEY_MACHINE_CERTPASS"-flags"].toInt(); + + if ((userAType.isEmpty() || userAType == QLatin1String(NM_L2TP_AUTHTYPE_PASSWORD)) && !(passType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("User Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_PASSWORD)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_PASSWORD))); + d->layout->addRow(label, lineEdit); + } else if (userAType == QLatin1String(NM_L2TP_AUTHTYPE_TLS) && !(userCertType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("User Certificate Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_USER_CERTPASS)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_USER_CERTPASS))); + d->layout->addRow(label, lineEdit); } - bool haveUserPassword = true; - if (!((NetworkManager::Setting::SecretFlags)data.value(NM_L2TP_KEY_PASSWORD"-flags").toInt()).testFlag(NetworkManager::Setting::NotRequired)) { - d->ui.leUserPassword->setText(secrets.value(QLatin1String(NM_L2TP_KEY_PASSWORD))); - } else { - d->ui.userNameLabel->setVisible(false); - d->ui.leUserName->setVisible(false); - d->ui.userPasswordLabel->setVisible(false); - d->ui.leUserPassword->setVisible(false); - haveUserPassword = false; + + if (machineAType == QLatin1String(NM_L2TP_AUTHTYPE_TLS)) { + if (!(machineCertType.testFlag(NetworkManager::Setting::NotRequired))) { + label = new QLabel(this); + label->setText(i18n("Machine Certificate Password:")); + lineEdit = new PasswordField(this); + lineEdit->setPasswordModeEnabled(true); + lineEdit->setProperty("nm_secrets_key", QLatin1String(NM_L2TP_KEY_MACHINE_CERTPASS)); + lineEdit->setText(secrets.value(QLatin1String(NM_L2TP_KEY_MACHINE_CERTPASS))); + d->layout->addRow(label, lineEdit); + } } - if (haveUserPassword && d->ui.leUserPassword->text().isEmpty()) { - d->ui.leUserPassword->setFocus(Qt::OtherFocusReason); + for (int i = 0; i < d->layout->rowCount(); i++) { + PasswordField *le = qobject_cast(d->layout->itemAt(i, QFormLayout::FieldRole)->widget()); + if (le && le->text().isEmpty()) { + le->setFocus(Qt::OtherFocusReason); + break; + } } } -QVariantMap L2tpAuthDialog::setting() const +QVariantMap L2tpAuthWidget::setting() const { - Q_D(const L2tpAuthDialog); + Q_D(const L2tpAuthWidget); NMStringMap secrets; - QVariantMap result; - - if (!d->ui.leUserPassword->text().isEmpty()) { - secrets.insert(NM_L2TP_KEY_PASSWORD, d->ui.leUserPassword->text()); + QVariantMap secretData; + for (int i = 0; i < d->layout->rowCount(); i++) { + PasswordField *le = qobject_cast(d->layout->itemAt(i, QFormLayout::FieldRole)->widget()); + if (le && !le->text().isEmpty()) { + const QString key = le->property("nm_secrets_key").toString(); + secrets.insert(key, le->text()); + } } - result.insert("secrets", QVariant::fromValue(secrets)); - - return result; + secretData.insert("secrets", QVariant::fromValue(secrets)); + return secretData; } diff --git a/vpn/l2tp/l2tpauth.ui b/vpn/l2tp/l2tpauth.ui deleted file mode 100644 --- a/vpn/l2tp/l2tpauth.ui +++ /dev/null @@ -1,71 +0,0 @@ - - - L2tpAuth - - - - 0 - 0 - 378 - 133 - - - - - 6 - - - - - Username: - - - leUserName - - - - - - - false - - - Set the name used for authenticating the local system to the peer to <name>. - - - - - - - User Password: - - - false - - - leUserPassword - - - - - - - Password passed to PPPD when prompted for it. - - - true - - - - - - - - PasswordField - QLineEdit -
passwordfield.h
-
-
- - -
diff --git a/vpn/l2tp/l2tpipsec.ui b/vpn/l2tp/l2tpipsec.ui --- a/vpn/l2tp/l2tpipsec.ui +++ b/vpn/l2tp/l2tpipsec.ui @@ -6,106 +6,363 @@ 0 0 - 382 - 254 + 538 + 660 - - - 6 - - - - - Enable IPsec tunnel to L2TP host - - - - - - - false - - - Gateway ID: - - - - - - - false - - - - - - - false - - - Pre-shared Key: - - - - - + + + - false - - true - - - - - - false - - - Phase1 algorithms: - - - - - - - false - - - - - - - false - - - Phase2 algorithms: - - - - - - - false - - - - - - - false + + Enable IPsec tunnel to L2TP host - - Enforce UDP encapsulation + + true true + + + + + + 0 + 0 + + + + Machine Authentication + + + + + + + + Type: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + cmbAuthType + + + + + + + + 0 + 0 + + + + Select an authentication mode. + + + + Pre-shared Key (PSK) + + + + + Certificates (TLS) + + + + + + + + + + + + + + Pre-shared Key: + + + presharedKey + + + + + + + + + <html><head/><body><p>Pre-shared key (PSK) secret.</p></body></html> + + + true + + + + + + + + + + + + + <html><head/><body><p>Certificate authority (CA) file in .pem, .der, .crt, .crt or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Machine Certificate: + + + false + + + machineCert + + + + + + + <html><head/><body><p>Certificate in .pem, .der or .p12 formats.</p></body></html> + + + *.pem *.der *.crt *.cer *.p12 + + + + + + + Private Key: + + + false + + + machineKey + + + + + + + <html><head/><body><p>Private key in .pem, .der, .key, .pk8 or .p12 formats.</p></body></html> + + + *.pem *der *.key *.pk8 *.p12 + + + + + + + Private Key Password: + + + machineKeyPassword + + + + + + + + + <html><head/><body><p>Password for private key or PKCS#12 certificate.</p></body></html> + + + true + + + + + + + + + CA Certificate: + + + false + + + machineCA + + + + + + + + + + + + + + Advanced + + + + + + Phase1 Algorithms: + + + ike + + + + + + + Phase2 Algorithms: + + + esp + + + + + + + <html><head/><body><p>Optional. A list of proposals for ESP - Quick Mode. The format is “enc-integ,enc-integ, …”.</p></body></html> + + + + + + + false + + + <html><head/><body><p>How long the keying channel of a connection should last before being renegotiated.</p></body></html> + + + hh:mm:ss + + + + + + + Phase1 Lifetime: + + + + + + + <html><head/><body><p>Optional. A list of proposals for IKE - Main Mode. The format is “enc-integ-group,enc-integ-group, …”.</p></body></html> + + + + + + + Phase2 Lifetime: + + + + + + + false + + + <html><head/><body><p>How long a particular instance of a connection (a set of encryption/authentication keys for user packets) should last, from successful negotiation to expiry.</p></body></html> + + + hh:mm:ss + + + + + + + <html><head/><body><p>Optional. How the IPsec server should be identified for authentication. Sometimes referred to as Peer ID or Gateway ID, also referred to as rightid by strongSwan, Libreswan, Openswan and FreeS/WAN. See strongSwan or Libreswan documentation for leftid/rightid syntax and identity parsing.</p></body></html> + + + + + + + <html><head/><body><p>IPComp compresses raw IP packets before they get encrypted. This saves some bandwidth, but uses more processing power.</p></body></html> + + + Use IP compression + + + + + + + <html><head/><body><p>Disable perfect forward security. Enable this option only if the server doesn’t support PFS.</p></body></html> + + + Disable PFS + + + + + + + <html><head/><body><p>Some firewalls block ESP traffic. Enforcing UDP encapsulation even if no NAT situation is detected might help in such cases.</p></body></html> + + + Enforce UDP encapsulation + + + false + + + + + + + Remote ID: + + + remoteId + + + + + + + + + + Qt::Vertical + + + + 510 + 0 + + + + + - + QDialogButtonBox::Cancel|QDialogButtonBox::Ok @@ -113,189 +370,109 @@ + buttonBox + gbEnableTunnelToHost + + + KUrlRequester + QWidget +
kurlrequester.h
+
PasswordField QLineEdit
passwordfield.h
+ + cmbAuthType + presharedKey + machineCA + machineCert + machineKey + machineKeyPassword + - cbEnableTunnelToHost - toggled(bool) - gatewayId - setEnabled(bool) - - - 159 - 14 - - - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - presharedKey - setEnabled(bool) + cmbAuthType + currentIndexChanged(int) + stackedWidget + setCurrentIndex(int) - 159 - 14 + 345 + 76 - 209 - 91 + 488 + 234 - cbEnableTunnelToHost - toggled(bool) - ike - setEnabled(bool) + buttonBox + accepted() + L2tpIpsecWidget + accept() 190 - 16 + 119 - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - esp - setEnabled(bool) - - 190 - 16 - - - 209 - 65 + 68 - cbEnableTunnelToHost - toggled(bool) - cbForceEncaps - setEnabled(bool) + buttonBox + rejected() + L2tpIpsecWidget + reject() 190 - 16 + 119 - 209 - 65 - - - - - cbEnableTunnelToHost - toggled(bool) - label_2 - setEnabled(bool) - - 190 - 16 - - - 75 - 87 + 68 - cbEnableTunnelToHost + cbIkelifetime toggled(bool) - label + ikelifetime setEnabled(bool) - 190 - 16 - - 86 - 49 - - - - - cbEnableTunnelToHost - toggled(bool) - label_3 - setEnabled(bool) - - - 190 - 16 + 393 - 64 - 125 + 314 + 393 - cbEnableTunnelToHost + cbSalifetime toggled(bool) - label_4 + salifetime setEnabled(bool) - 190 - 16 - - - 64 - 163 - - - - - buttonBox - accepted() - L2tpIpsecWidget - accept() - - - 190 - 119 - - - 190 - 68 - - - - - buttonBox - rejected() - L2tpIpsecWidget - reject() - - - 190 - 119 + 86 + 418 - 190 - 68 + 314 + 418 diff --git a/vpn/l2tp/l2tpipsecwidget.h b/vpn/l2tp/l2tpipsecwidget.h --- a/vpn/l2tp/l2tpipsecwidget.h +++ b/vpn/l2tp/l2tpipsecwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -33,15 +34,29 @@ class L2tpIpsecWidget : public QDialog { Q_OBJECT + + enum AuthType {PSK = 0, TLS}; + enum IpsecDaemonType {NoIpsecDaemon, Libreswan, Strongswan, Openswan, UnknownIpsecDaemon}; + public: explicit L2tpIpsecWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); ~L2tpIpsecWidget() override; NMStringMap setting() const; + NMStringMap secrets() const; + + static bool hasIpsecDaemon(); + +private Q_SLOTS: + void updateStartDirUrl(const QUrl &); + void setDefaultIkelifetime(bool isChecked); + void setDefaultSalifetime(bool isChecked); + void resizeStackedWidget(int currentIndex); private: void loadConfig(const NetworkManager::VpnSetting::Ptr &setting); Ui::L2tpIpsecWidget * m_ui; + static IpsecDaemonType m_ipsecDaemonType; }; #endif // PLASMA_NM_L2TP_IPSEC_WIDGET_H diff --git a/vpn/l2tp/l2tpipsecwidget.cpp b/vpn/l2tp/l2tpipsecwidget.cpp --- a/vpn/l2tp/l2tpipsecwidget.cpp +++ b/vpn/l2tp/l2tpipsecwidget.cpp @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -18,6 +19,8 @@ License along with this library. If not, see . */ +#include +#include #include "l2tpipsecwidget.h" #include "ui_l2tpipsec.h" #include "nm-l2tp-service.h" @@ -25,17 +28,39 @@ #include #include +#define DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS 3 +#define DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS 1 + +#define DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS 1 +#define DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS 8 + L2tpIpsecWidget::L2tpIpsecWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) : QDialog(parent) , m_ui(new Ui::L2tpIpsecWidget) { m_ui->setupUi(this); + m_ui->machineKeyPassword->setPasswordOptionsEnabled(true); + m_ui->machineKeyPassword->setPasswordNotRequiredEnabled(true); + + // use requesters' urlSelected signals to set other requester's startDirs to save clicking + // around the filesystem, also if it is a .p12 file, set the other URLs to that .p12 file. + QList requesters; + requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey; + for (const KUrlRequester *requester : requesters) { + connect(requester, &KUrlRequester::urlSelected, this, &L2tpIpsecWidget::updateStartDirUrl); + } + + connect(m_ui->cbIkelifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultIkelifetime); + connect(m_ui->cbSalifetime, &QCheckBox::toggled, this, &L2tpIpsecWidget::setDefaultSalifetime); + connect(m_ui->cmbAuthType, QOverload::of(&QComboBox::currentIndexChanged), this, &L2tpIpsecWidget::resizeStackedWidget); setWindowTitle(i18n("L2TP IPsec Options")); KAcceleratorManager::manage(this); loadConfig(setting); + + resizeStackedWidget(m_ui->cmbAuthType->currentIndex()); } L2tpIpsecWidget::~L2tpIpsecWidget() @@ -45,35 +70,177 @@ void L2tpIpsecWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting) { - if (setting->data().value(NM_L2TP_KEY_IPSEC_ENABLE) == "yes") { - m_ui->cbEnableTunnelToHost->setChecked(true); - m_ui->gatewayId->setText(setting->data().value(NM_L2TP_KEY_IPSEC_GATEWAY_ID)); - m_ui->presharedKey->setText(setting->data().value(NM_L2TP_KEY_IPSEC_PSK)); - m_ui->ike->setText(setting->data().value(NM_L2TP_KEY_IPSEC_IKE)); - m_ui->esp->setText(setting->data().value(NM_L2TP_KEY_IPSEC_ESP)); - if (setting->data().value(NM_L2TP_KEY_IPSEC_FORCEENCAPS) == "yes" ) { + const QString yesString = QLatin1String("yes"); + const QString noString = QLatin1String("no"); + + // General settings + const NMStringMap dataMap = setting->data(); + + if (!hasIpsecDaemon()) { + m_ui->gbEnableTunnelToHost->setChecked(false); + m_ui->gbEnableTunnelToHost->setDisabled(true); + } else if (dataMap[NM_L2TP_KEY_IPSEC_ENABLE] == yesString) { + m_ui->gbEnableTunnelToHost->setChecked(true); + + if (dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE].isEmpty() + || dataMap[NM_L2TP_KEY_MACHINE_AUTH_TYPE] == QLatin1String(NM_L2TP_AUTHTYPE_PSK)) { + m_ui->cmbAuthType->setCurrentIndex(AuthType::PSK); + m_ui->stackedWidget->setCurrentIndex(AuthType::PSK); + + // *SWAN support Base64 encoded PSKs that are prefixed with "0s" + QString psk = dataMap[NM_L2TP_KEY_IPSEC_PSK]; + if (psk.length() > 2 && psk.startsWith(QLatin1String("0s"))) { + m_ui->presharedKey->setText(QByteArray::fromBase64(psk.mid(2).toUtf8())); + } else { + m_ui->presharedKey->setText(psk); + } + + } else { // NM_L2TP_AUTHTYPE_TLS + m_ui->cmbAuthType->setCurrentIndex(AuthType::TLS); + m_ui->stackedWidget->setCurrentIndex(AuthType::TLS); + + m_ui->machineCA->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CA])); + m_ui->machineCert->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_CERT])); + m_ui->machineKey->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_MACHINE_KEY])); + + const NetworkManager::Setting::SecretFlags machineKeyPassType = static_cast(dataMap[NM_L2TP_KEY_MACHINE_CERTPASS"-flags"].toInt()); + if (machineKeyPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::StoreForUser); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotSaved)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::AlwaysAsk); + } else if (machineKeyPassType.testFlag(NetworkManager::Setting::NotRequired)) { + m_ui->machineKeyPassword->setPasswordOption(PasswordField::NotRequired); + } + } + + if (!dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID].isEmpty()) { + m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_GATEWAY_ID]); + } else { + m_ui->remoteId->setText(dataMap[NM_L2TP_KEY_IPSEC_REMOTE_ID]); + } + m_ui->ike->setText(dataMap[NM_L2TP_KEY_IPSEC_IKE]); + m_ui->esp->setText(dataMap[NM_L2TP_KEY_IPSEC_ESP]); + + if (!dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].isEmpty()) { + int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_IKELIFETIME].toInt(); + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + QTime lifetime = QTime(hours, minutes, seconds); + + m_ui->ikelifetime->setTime(lifetime); + m_ui->ikelifetime->setEnabled(true); + m_ui->cbIkelifetime->setChecked(true); + } else { + setDefaultIkelifetime(false); + m_ui->ikelifetime->setEnabled(false); + m_ui->cbIkelifetime->setChecked(false); + } + + if (!dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].isEmpty()) { + int totalSeconds = dataMap[NM_L2TP_KEY_IPSEC_SALIFETIME].toInt(); + int hours = totalSeconds / 3600; + int minutes = (totalSeconds % 3600) / 60; + int seconds = totalSeconds % 60; + QTime lifetime = QTime(hours, minutes, seconds); + + m_ui->salifetime->setTime(lifetime); + m_ui->salifetime->setEnabled(true); + m_ui->cbSalifetime->setChecked(true); + } else { + setDefaultSalifetime(false); + m_ui->salifetime->setEnabled(false); + m_ui->cbSalifetime->setChecked(false); + } + + if (dataMap[NM_L2TP_KEY_IPSEC_FORCEENCAPS] == yesString ) { m_ui->cbForceEncaps->setChecked(true); } else { m_ui->cbForceEncaps->setChecked(false); } + if (dataMap[NM_L2TP_KEY_IPSEC_IPCOMP] == yesString ) { + m_ui->cbIPComp->setChecked(true); + } else { + m_ui->cbIPComp->setChecked(false); + } + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + if (dataMap[NM_L2TP_KEY_IPSEC_PFS] == noString ) { + m_ui->cbPFS->setChecked(true); + } else { + m_ui->cbPFS->setChecked(false); + } + } else { + m_ui->cbPFS->setChecked(false); + m_ui->cbPFS->setDisabled(true); + m_ui->cbPFS->setToolTip(""); + } } else { - m_ui->cbEnableTunnelToHost->setChecked(false); + m_ui->gbEnableTunnelToHost->setChecked(false); + setDefaultIkelifetime(false); + setDefaultSalifetime(false); + if (m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + m_ui->cbPFS->setDisabled(true); + m_ui->cbPFS->setToolTip(""); + } } } NMStringMap L2tpIpsecWidget::setting() const { NMStringMap result; + const QString yesString = QLatin1String("yes"); + const QString noString = QLatin1String("no"); + + if (m_ui->gbEnableTunnelToHost->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_ENABLE, yesString); + + if (m_ui->cmbAuthType->currentIndex() == AuthType::PSK) { + // NetworkManager-l2tp < 1.2.12 does not support Base64 PSK + // For backwards compatibilty don't use Base64 PSK for now. + if (!m_ui->presharedKey->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_PSK, m_ui->presharedKey->text()); + // *SWAN support Base64 encoded PSKs that are prefixed with "0s" + /* + QString psk = QLatin1String("0s"); + psk.append(m_ui->presharedKey->text().toUtf8().toBase64()); + result.insert(NM_L2TP_KEY_IPSEC_PSK, psk); + */ + } + } else { // AuthType::TLS - if (m_ui->cbEnableTunnelToHost->isChecked()) { - result.insert(NM_L2TP_KEY_IPSEC_ENABLE, "yes"); + result.insert(NM_L2TP_KEY_MACHINE_AUTH_TYPE, NM_L2TP_AUTHTYPE_TLS); - if (!m_ui->gatewayId->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_GATEWAY_ID, m_ui->gatewayId->text()); + result.insert(NM_L2TP_KEY_MACHINE_CA, m_ui->machineCA->url().toLocalFile()); + result.insert(NM_L2TP_KEY_MACHINE_CERT, m_ui->machineCert->url().toLocalFile()); + result.insert(NM_L2TP_KEY_MACHINE_KEY, m_ui->machineKey->url().toLocalFile()); + + switch (m_ui->machineKeyPassword->passwordOption()) { + case PasswordField::StoreForAllUsers: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::None)); + break; + case PasswordField::StoreForUser: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::AgentOwned)); + break; + case PasswordField::AlwaysAsk: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::NotSaved)); + break; + case PasswordField::NotRequired: + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS"-flags", + QString::number(NetworkManager::Setting::NotRequired)); + break; + }; } - if (!m_ui->presharedKey->text().isEmpty()) { - result.insert(NM_L2TP_KEY_IPSEC_PSK, m_ui->presharedKey->text()); + // NetworkManager-l2tp 1.2.12 recommends NM_L2TP_KEY_IPSEC_REMOTE_ID + // instead of deprecated NM_L2TP_KEY_IPSEC_GATEWAY_ID + // For backwards compatibilty use NM_L2TP_KEY_IPSEC_GATEWAY_ID for now. + if (!m_ui->remoteId->text().isEmpty()) { + result.insert(NM_L2TP_KEY_IPSEC_GATEWAY_ID, m_ui->remoteId->text()); } if (!m_ui->ike->text().isEmpty()) { @@ -85,9 +252,155 @@ } if (m_ui->cbForceEncaps->isChecked()) { - result.insert(NM_L2TP_KEY_IPSEC_FORCEENCAPS, "yes"); + result.insert(NM_L2TP_KEY_IPSEC_FORCEENCAPS, yesString); + } + + if (m_ui->cbIkelifetime->isChecked()) { + int totalSeconds = m_ui->ikelifetime->time().hour() * 3600 + m_ui->ikelifetime->time().minute() * 60 + m_ui->ikelifetime->time().second(); + + result.insert(NM_L2TP_KEY_IPSEC_IKELIFETIME, QString::number(totalSeconds)); + } + + if (m_ui->cbSalifetime->isChecked()) { + int totalSeconds = m_ui->salifetime->time().hour() * 3600 + m_ui->salifetime->time().minute() * 60 + m_ui->salifetime->time().second(); + result.insert(NM_L2TP_KEY_IPSEC_SALIFETIME, QString::number(totalSeconds)); + } + + if (m_ui->cbIPComp->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_IPCOMP, yesString); + } + + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + if (m_ui->cbPFS->isChecked()) { + result.insert(NM_L2TP_KEY_IPSEC_PFS, noString); + } + } + } + + return result; +} + +NMStringMap L2tpIpsecWidget::secrets() const +{ + NMStringMap result; + + if (m_ui->gbEnableTunnelToHost->isChecked()) { + if (m_ui->cmbAuthType->currentIndex() == AuthType::TLS) { + + // private key password + if (!m_ui->machineKeyPassword->text().isEmpty()) { + result.insert(NM_L2TP_KEY_MACHINE_CERTPASS, m_ui->machineKeyPassword->text()); + } } } return result; } + +void L2tpIpsecWidget::updateStartDirUrl(const QUrl &url) +{ + QList requesters; + requesters << m_ui->machineCA << m_ui->machineCert << m_ui->machineKey; + bool isP12 = url.toString().endsWith(QLatin1String(".p12")); + + for (KUrlRequester *requester : requesters) { + requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); + if (isP12) { + requester->setUrl(url); + } + } +} + +void L2tpIpsecWidget::setDefaultIkelifetime(bool isChecked) +{ + if (!isChecked) { + QTime lifetime; + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_IKELIFETIME_HOURS, 0, 0); + } else { + lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_IKELIFETIME_HOURS, 0, 0); + } + m_ui->ikelifetime->setTime(lifetime); + } +} + +void L2tpIpsecWidget::setDefaultSalifetime(bool isChecked) +{ + if (!isChecked) { + QTime lifetime; + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan) { + lifetime = QTime(DEFAULT_IPSEC_LIBRESWAN_SALIFETIME_HOURS, 0, 0); + } else { + lifetime = QTime(DEFAULT_IPSEC_STRONGSWAN_LIFETIME_HOURS, 0, 0); + } + m_ui->salifetime->setTime(lifetime); + } +} + +void L2tpIpsecWidget::resizeStackedWidget(int currentIndex) +{ + m_ui->stackedWidget->setCurrentIndex(currentIndex); + for (int i = 0; i < m_ui->stackedWidget->count(); ++i) { + QSizePolicy::Policy policy; + + if (i == currentIndex) { + policy = QSizePolicy::Preferred; + } else { + policy = QSizePolicy::Ignored; + } + m_ui->stackedWidget->widget(i)->setSizePolicy(QSizePolicy::Preferred, policy); + } + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + resize(width(), sizeHint().height()); +} + +L2tpIpsecWidget::IpsecDaemonType L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon; + +bool L2tpIpsecWidget::hasIpsecDaemon() +{ + // NetworkManager-l2tp currently only supports libreswan and strongswan + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan + || m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + return true; + } + + QString ipsecBinary = QStandardPaths::findExecutable("ipsec", + QStringList() << "/sbin" << "/usr/sbin"); + + // On some Linux distributions, ipsec executable has been renamed strongswan + if (ipsecBinary.isEmpty()) { + ipsecBinary = QStandardPaths::findExecutable("strongswan", + QStringList() << "/sbin" << "/usr/sbin"); + } + + if (ipsecBinary.isEmpty()) { + m_ipsecDaemonType = IpsecDaemonType::NoIpsecDaemon; + return false; + } + + QProcess ipsecVersionProcess; + ipsecVersionProcess.setProgram(ipsecBinary); + ipsecVersionProcess.setArguments(QStringList() << "--version"); + ipsecVersionProcess.start(); + ipsecVersionProcess.waitForFinished(-1); + + if (ipsecVersionProcess.exitStatus() == QProcess::NormalExit) { + QString ipsecStdout = ipsecVersionProcess.readAllStandardOutput(); + + if (ipsecStdout.contains(" strongSwan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Strongswan; + } else if (ipsecStdout.contains(" Libreswan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Libreswan; + } else if (ipsecStdout.contains(" Openswan ", Qt::CaseSensitive)) { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::Openswan; + } else { + L2tpIpsecWidget::m_ipsecDaemonType = IpsecDaemonType::UnknownIpsecDaemon; + } + } + + if (m_ipsecDaemonType == IpsecDaemonType::Libreswan + || m_ipsecDaemonType == IpsecDaemonType::Strongswan) { + return true; + } + return false; +} diff --git a/vpn/l2tp/l2tppppwidget.h b/vpn/l2tp/l2tppppwidget.h --- a/vpn/l2tp/l2tppppwidget.h +++ b/vpn/l2tp/l2tppppwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -34,7 +35,7 @@ { Q_OBJECT public: - explicit L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr); + explicit L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent = nullptr, bool need_peer_eap = false); ~L2tpPPPWidget() override; NMStringMap setting() const; @@ -42,6 +43,7 @@ private: void loadConfig(const NetworkManager::VpnSetting::Ptr &setting); Ui::L2tpPppWidget * m_ui; + bool m_need_peer_eap; }; #endif // PLASMA_NM_L2TP_PPP_WIDGET_H diff --git a/vpn/l2tp/l2tppppwidget.cpp b/vpn/l2tp/l2tppppwidget.cpp --- a/vpn/l2tp/l2tppppwidget.cpp +++ b/vpn/l2tp/l2tppppwidget.cpp @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,9 +26,10 @@ #include #include -L2tpPPPWidget::L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) +L2tpPPPWidget::L2tpPPPWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent, bool need_peer_eap) : QDialog(parent) , m_ui(new Ui::L2tpPppWidget) + , m_need_peer_eap(need_peer_eap) { m_ui->setupUi(this); @@ -45,28 +47,34 @@ void L2tpPPPWidget::loadConfig(const NetworkManager::VpnSetting::Ptr &setting) { + const QString yesString = QLatin1String("yes"); + // General settings const NMStringMap dataMap = setting->data(); - // Authentication options - const QString yesString = QLatin1String("yes"); - bool refuse_pap = (dataMap[NM_L2TP_KEY_REFUSE_PAP] == yesString); - bool refuse_chap = (dataMap[NM_L2TP_KEY_REFUSE_CHAP] == yesString); - bool refuse_mschap = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAP] == yesString); - bool refuse_mschapv2 = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAPV2] == yesString); - bool refuse_eap = (dataMap[NM_L2TP_KEY_REFUSE_EAP] == yesString); - - QListWidgetItem * item = nullptr; - item = m_ui->listWidget->item(0); // PAP - item->setCheckState(refuse_pap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(1); // CHAP - item->setCheckState(refuse_chap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(2); // MSCHAP - item->setCheckState(refuse_mschap ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(3); // MSCHAPv2 - item->setCheckState(refuse_mschapv2 ? Qt::Unchecked : Qt::Checked); - item = m_ui->listWidget->item(4); // EAP - item->setCheckState(refuse_eap ? Qt::Unchecked : Qt::Checked); + if (m_need_peer_eap) { + m_ui->grp_authenfication->setVisible(false); + resize(width(), sizeHint().height()); + } else { + // Authentication options + bool refuse_pap = (dataMap[NM_L2TP_KEY_REFUSE_PAP] == yesString); + bool refuse_chap = (dataMap[NM_L2TP_KEY_REFUSE_CHAP] == yesString); + bool refuse_mschap = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAP] == yesString); + bool refuse_mschapv2 = (dataMap[NM_L2TP_KEY_REFUSE_MSCHAPV2] == yesString); + bool refuse_eap = (dataMap[NM_L2TP_KEY_REFUSE_EAP] == yesString); + + QListWidgetItem * item = nullptr; + item = m_ui->listWidget->item(0); // PAP + item->setCheckState(refuse_pap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(1); // CHAP + item->setCheckState(refuse_chap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(2); // MSCHAP + item->setCheckState(refuse_mschap ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(3); // MSCHAPv2 + item->setCheckState(refuse_mschapv2 ? Qt::Unchecked : Qt::Checked); + item = m_ui->listWidget->item(4); // EAP + item->setCheckState(refuse_eap ? Qt::Unchecked : Qt::Checked); + } // Cryptography and compression const bool mppe = (dataMap[NM_L2TP_KEY_REQUIRE_MPPE] == yesString); @@ -117,29 +125,30 @@ NMStringMap L2tpPPPWidget::setting() const { NMStringMap result; - - QListWidgetItem * item = nullptr; - item = m_ui->listWidget->item(0); // PAP const QString yesString = QLatin1String("yes"); - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_PAP, yesString); - } - item = m_ui->listWidget->item(1); // CHAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_CHAP, yesString); - } - item = m_ui->listWidget->item(2); // MSCHAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_MSCHAP, yesString); - } - item = m_ui->listWidget->item(3); // MSCHAPv2 - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_MSCHAPV2, yesString); - } - item = m_ui->listWidget->item(4); // EAP - if (item->checkState() == Qt::Unchecked) { - result.insert(NM_L2TP_KEY_REFUSE_EAP, yesString); + if (!m_need_peer_eap) { + QListWidgetItem * item = nullptr; + item = m_ui->listWidget->item(0); // PAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_PAP, yesString); + } + item = m_ui->listWidget->item(1); // CHAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_CHAP, yesString); + } + item = m_ui->listWidget->item(2); // MSCHAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_MSCHAP, yesString); + } + item = m_ui->listWidget->item(3); // MSCHAPv2 + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_MSCHAPV2, yesString); + } + item = m_ui->listWidget->item(4); // EAP + if (item->checkState() == Qt::Unchecked) { + result.insert(NM_L2TP_KEY_REFUSE_EAP, yesString); + } } // Cryptography and compression diff --git a/vpn/l2tp/l2tpwidget.h b/vpn/l2tp/l2tpwidget.h --- a/vpn/l2tp/l2tpwidget.h +++ b/vpn/l2tp/l2tpwidget.h @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -33,6 +34,9 @@ class L2tpWidget : public SettingWidget { Q_OBJECT + + enum AuthType {Password = 0, TLS}; + public: explicit L2tpWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget* parent = nullptr, Qt::WindowFlags f = {}); ~L2tpWidget() override; @@ -45,10 +49,9 @@ bool isValid() const override; private Q_SLOTS: - void userPasswordTypeChanged(int index); + void updateStartDirUrl(const QUrl &); void showIpsec(); void showPpp(); - void certStateChanged(); private: Ui::L2tpWidget * m_ui; diff --git a/vpn/l2tp/l2tpwidget.cpp b/vpn/l2tp/l2tpwidget.cpp --- a/vpn/l2tp/l2tpwidget.cpp +++ b/vpn/l2tp/l2tpwidget.cpp @@ -1,5 +1,6 @@ /* Copyright 2013 Jan Grulich + Copyright 2020 Douglas Kosovic This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -39,8 +40,22 @@ m_ui->setupUi(this); m_ui->password->setPasswordOptionsEnabled(true); + m_ui->userKeyPassword->setPasswordOptionsEnabled(true); + m_ui->userKeyPassword->setPasswordNotRequiredEnabled(true); + + // use requesters' urlSelected signals to set other requester's startDirs to save clicking + // around the filesystem, also if it is a .p12 file, set the other URLs to that .p12 file. + QList requesters; + requesters << m_ui->userCA << m_ui->userCert << m_ui->userKey; + for (const KUrlRequester *requester : requesters) { + connect(requester, &KUrlRequester::urlSelected, this, &L2tpWidget::updateStartDirUrl); + } - connect(m_ui->btnIPSecSettings, &QPushButton::clicked, this, &L2tpWidget::showIpsec); + if (L2tpIpsecWidget::hasIpsecDaemon()) { + connect(m_ui->btnIPSecSettings, &QPushButton::clicked, this, &L2tpWidget::showIpsec); + } else { + m_ui->btnIPSecSettings->setDisabled(true); + } connect(m_ui->btnPPPSettings, &QPushButton::clicked, this, &L2tpWidget::showPpp); // Connect for setting check @@ -48,7 +63,6 @@ // Connect for validity check connect(m_ui->gateway, &QLineEdit::textChanged, this, &L2tpWidget::slotWidgetChanged); - connect(m_ui->cbUseCertificate, &QCheckBox::stateChanged, this, &L2tpWidget::certStateChanged); KAcceleratorManager::manage(this); @@ -68,43 +82,44 @@ { Q_UNUSED(setting); - const NMStringMap data = m_setting->data(); - - if (data.contains(NM_L2TP_KEY_GATEWAY)) { - m_ui->gateway->setText(data.value(NM_L2TP_KEY_GATEWAY)); - } - - if (data.contains(NM_L2TP_KEY_USER)) { - m_ui->username->setText(data.value(NM_L2TP_KEY_USER)); - } - - const NetworkManager::Setting::SecretFlags userPassType = static_cast(data.value(NM_L2TP_KEY_PASSWORD"-flags").toInt()); - if (userPassType.testFlag(NetworkManager::Setting::None)) { - m_ui->password->setPasswordOption(PasswordField::StoreForAllUsers); - } else if (userPassType.testFlag(NetworkManager::Setting::AgentOwned)) { - m_ui->password->setPasswordOption(PasswordField::StoreForUser); - } else { - m_ui->password->setPasswordOption(PasswordField::AlwaysAsk); - } + const NMStringMap dataMap = m_setting->data(); - if (data.contains(NM_L2TP_KEY_DOMAIN)) { - m_ui->domain->setText(data.value(NM_L2TP_KEY_DOMAIN)); - } + m_ui->gateway->setText(dataMap[NM_L2TP_KEY_GATEWAY]); - if (data.contains(NM_L2TP_KEY_CERT_CA)) { - m_ui->urCACertificate->setText(data.value(NM_L2TP_KEY_CERT_CA)); - } - - if (data.contains(NM_L2TP_KEY_CERT_PUB)) { - m_ui->urCertificate->setText(data.value(NM_L2TP_KEY_CERT_PUB)); - } + if (dataMap[NM_L2TP_KEY_USER_AUTH_TYPE].isEmpty() || dataMap[NM_L2TP_KEY_USER_AUTH_TYPE] == QLatin1String(NM_L2TP_AUTHTYPE_PASSWORD)) { + m_ui->cmbAuthType->setCurrentIndex(AuthType::Password); + m_ui->stackedWidget->setCurrentIndex(AuthType::Password); + m_ui->username->setText(dataMap[NM_L2TP_KEY_USER]); - if (data.contains(NM_L2TP_KEY_CERT_KEY)) { - m_ui->urPrivateKey->setText(data.value(NM_L2TP_KEY_CERT_KEY)); - } + const NetworkManager::Setting::SecretFlags userPassType = static_cast(dataMap[NM_L2TP_KEY_PASSWORD"-flags"].toInt()); + if (userPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->password->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (userPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->password->setPasswordOption(PasswordField::StoreForUser); + } else { + m_ui->password->setPasswordOption(PasswordField::AlwaysAsk); + } - if (data.value(NM_L2TP_KEY_USE_CERT) == QLatin1String("yes")) { - m_ui->cbUseCertificate->setChecked(true); + m_ui->domain->setText(dataMap[NM_L2TP_KEY_DOMAIN]); + + } else { // NM_L2TP_AUTHTYPE_TLS + m_ui->cmbAuthType->setCurrentIndex(AuthType::TLS); + m_ui->stackedWidget->setCurrentIndex(AuthType::TLS); + + m_ui->userCA->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_CA])); + m_ui->userCert->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_CERT])); + m_ui->userKey->setUrl(QUrl::fromLocalFile(dataMap[NM_L2TP_KEY_USER_KEY])); + + const NetworkManager::Setting::SecretFlags userKeyPassType = static_cast(dataMap[NM_L2TP_KEY_USER_CERTPASS"-flags"].toInt()); + if (userKeyPassType.testFlag(NetworkManager::Setting::None)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::StoreForAllUsers); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::AgentOwned)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::StoreForUser); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::NotSaved)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::AlwaysAsk); + } else if (userKeyPassType.testFlag(NetworkManager::Setting::NotRequired)) { + m_ui->userKeyPassword->setPasswordOption(PasswordField::NotRequired); + } } loadSecrets(setting); @@ -115,66 +130,57 @@ NetworkManager::VpnSetting::Ptr vpnSetting = setting.staticCast(); if (vpnSetting) { + const QString authType = m_setting->data().value(NM_L2TP_KEY_USER_AUTH_TYPE); const NMStringMap secrets = vpnSetting->secrets(); - const QString userPassword = secrets.value(NM_L2TP_KEY_PASSWORD); - if (!userPassword.isEmpty()) { - m_ui->password->setText(userPassword); + + if (authType == QLatin1String(NM_L2TP_AUTHTYPE_TLS)) { + m_ui->userKeyPassword->setText(secrets.value(NM_L2TP_KEY_USER_CERTPASS)); + } else { // NM_L2TP_AUTHTYPE_PASSWORD + m_ui->password->setText(secrets.value(NM_L2TP_KEY_PASSWORD)); } } } + QVariantMap L2tpWidget::setting() const { NetworkManager::VpnSetting setting; setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_L2TP)); NMStringMap data; + NMStringMap secrets; + if (!m_tmpIpsecSetting.isNull()) { data = m_tmpIpsecSetting->data(); + secrets = m_tmpIpsecSetting->secrets(); } else { // retrieve the settings if the dialog has not been opened QScopedPointer ipsec(new L2tpIpsecWidget(m_setting, nullptr)); data = ipsec->setting(); + secrets = ipsec->secrets(); } if (!m_tmpPppSetting.isNull()) { data.unite(m_tmpPppSetting->data()); } else { + bool need_peer_eap = m_ui->cmbAuthType->currentIndex() != AuthType::Password; // retrieve the settings if the dialog has not been opened - QScopedPointer ppp(new L2tpPPPWidget(m_setting, nullptr)); + QScopedPointer ppp(new L2tpPPPWidget(m_setting, nullptr, need_peer_eap)); data.unite(ppp->setting()); } - NMStringMap secrets; - if (!m_ui->gateway->text().isEmpty()) { data.insert(NM_L2TP_KEY_GATEWAY, m_ui->gateway->text()); } - if (m_ui->cbUseCertificate->isChecked()) { - data.insert(NM_L2TP_KEY_USE_CERT, "yes"); - - if (!m_ui->urCACertificate->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_CA, m_ui->urCACertificate->text()); - } - - if (!m_ui->urCertificate->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_PUB, m_ui->urCertificate->text()); - } - - if (!m_ui->urPrivateKey->text().isEmpty()) { - data.insert(NM_L2TP_KEY_CERT_KEY, m_ui->urPrivateKey->text()); - } - - data.insert(NM_L2TP_KEY_PASSWORD"-flags", QString::number(NetworkManager::Setting::NotRequired)); - - } else { - + if (m_ui->cmbAuthType->currentIndex() == AuthType::Password) { if (!m_ui->username->text().isEmpty()) { data.insert(NM_L2TP_KEY_USER, m_ui->username->text()); } - if (m_ui->password->isEnabled() && !m_ui->password->text().isEmpty()) { + if (!m_ui->password->text().isEmpty()) { secrets.insert(NM_L2TP_KEY_PASSWORD, m_ui->password->text()); + } else { + secrets.remove(NM_L2TP_KEY_PASSWORD); } switch (m_ui->password->passwordOption()) { @@ -191,6 +197,36 @@ if (!m_ui->domain->text().isEmpty()) { data.insert(NM_L2TP_KEY_DOMAIN, m_ui->domain->text()); } + + } else { // EnumAuthType::TLS + + data.insert(NM_L2TP_KEY_USER_AUTH_TYPE, NM_L2TP_AUTHTYPE_TLS); + + data.insert(NM_L2TP_KEY_USER_CA, m_ui->userCA->url().toLocalFile()); + data.insert(NM_L2TP_KEY_USER_CERT, m_ui->userCert->url().toLocalFile()); + data.insert(NM_L2TP_KEY_USER_KEY, m_ui->userKey->url().toLocalFile()); + + // key password + if (!m_ui->userKeyPassword->text().isEmpty()) { + secrets.insert(NM_L2TP_KEY_USER_CERTPASS, m_ui->userKeyPassword->text()); + } else { + secrets.remove(NM_L2TP_KEY_USER_CERTPASS); + } + + switch (m_ui->userKeyPassword->passwordOption()) { + case PasswordField::StoreForAllUsers: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::None)); + break; + case PasswordField::StoreForUser: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::AgentOwned)); + break; + case PasswordField::AlwaysAsk: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::NotSaved)); + break; + case PasswordField::NotRequired: + data.insert(NM_L2TP_KEY_USER_CERTPASS"-flags", QString::number(NetworkManager::Setting::NotRequired)); + break; + }; } setting.setData(data); @@ -198,10 +234,18 @@ return setting.toMap(); } - -void L2tpWidget::userPasswordTypeChanged(int index) +void L2tpWidget::updateStartDirUrl(const QUrl &url) { - m_ui->password->setEnabled(index == SettingWidget::EnumPasswordStorageType::Store); + QList requesters; + requesters << m_ui->userCA << m_ui->userCert << m_ui->userKey; + bool isP12 = url.toString().endsWith(QLatin1String(".p12")); + + for (KUrlRequester *requester : requesters) { + requester->setStartDir(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash)); + if (isP12) { + requester->setUrl(url); + } + } } void L2tpWidget::showIpsec() @@ -235,10 +279,11 @@ void L2tpWidget::showPpp() { QPointer ipsec; + bool need_peer_eap = m_ui->cmbAuthType->currentIndex() != AuthType::Password; if (m_tmpPppSetting.isNull()) { - ipsec = new L2tpPPPWidget(m_setting, this); + ipsec = new L2tpPPPWidget(m_setting, this, need_peer_eap); } else { - ipsec = new L2tpPPPWidget(m_tmpPppSetting, this); + ipsec = new L2tpPPPWidget(m_tmpPppSetting, this, need_peer_eap); } connect(ipsec.data(), &L2tpPPPWidget::accepted, [ipsec, this] () { @@ -264,22 +309,3 @@ { return !m_ui->gateway->text().isEmpty(); } - -void L2tpWidget::certStateChanged() -{ - if (m_ui->cbUseCertificate->isChecked()) { - m_ui->urCACertificate->setEnabled(true); - m_ui->urCertificate->setEnabled(true); - m_ui->urPrivateKey->setEnabled(true); - m_ui->username->setEnabled(false); - m_ui->password->setEnabled(false); - m_ui->domain->setEnabled(false); - } else { - m_ui->urCACertificate->setEnabled(false); - m_ui->urCertificate->setEnabled(false); - m_ui->urPrivateKey->setEnabled(false); - m_ui->username->setEnabled(true); - m_ui->password->setEnabled(true); - m_ui->domain->setEnabled(true); - } -} diff --git a/vpn/l2tp/nm-l2tp-service.h b/vpn/l2tp/nm-l2tp-service.h --- a/vpn/l2tp/nm-l2tp-service.h +++ b/vpn/l2tp/nm-l2tp-service.h @@ -1,22 +1,8 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +// SPDX-License-Identifier: GPL-2.0+ /* nm-l2tp-service - L2TP VPN integration with NetworkManager * * Dan Williams * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * * Copyright 2008, 2014 Red Hat, Inc. */ @@ -34,15 +20,16 @@ #define NM_DBUS_PATH_L2TP_PPP "/org/freedesktop/NetworkManager/l2tp/ppp" #define NM_L2TP_KEY_GATEWAY "gateway" +#define NM_L2TP_KEY_USER_AUTH_TYPE "user-auth-type" #define NM_L2TP_KEY_USER "user" #define NM_L2TP_KEY_PASSWORD "password" -#define NM_L2TP_KEY_USE_CERT "use-cert" -#define NM_L2TP_KEY_CERT_PUB "cert-pub" -#define NM_L2TP_KEY_CERT_CA "cert-ca" -#define NM_L2TP_KEY_CERT_KEY "cert-key" +#define NM_L2TP_KEY_DOMAIN "domain" +#define NM_L2TP_KEY_USER_CA "user-ca" +#define NM_L2TP_KEY_USER_CERT "user-cert" +#define NM_L2TP_KEY_USER_KEY "user-key" +#define NM_L2TP_KEY_USER_CERTPASS "user-certpass" #define NM_L2TP_KEY_MTU "mtu" #define NM_L2TP_KEY_MRU "mru" -#define NM_L2TP_KEY_DOMAIN "domain" #define NM_L2TP_KEY_REFUSE_EAP "refuse-eap" #define NM_L2TP_KEY_REFUSE_PAP "refuse-pap" #define NM_L2TP_KEY_REFUSE_CHAP "refuse-chap" @@ -60,13 +47,30 @@ #define NM_L2TP_KEY_LCP_ECHO_FAILURE "lcp-echo-failure" #define NM_L2TP_KEY_LCP_ECHO_INTERVAL "lcp-echo-interval" #define NM_L2TP_KEY_UNIT_NUM "unit" - +#define NM_L2TP_KEY_MACHINE_AUTH_TYPE "machine-auth-type" +#define NM_L2TP_KEY_MACHINE_CA "machine-ca" +#define NM_L2TP_KEY_MACHINE_CERT "machine-cert" +#define NM_L2TP_KEY_MACHINE_KEY "machine-key" +#define NM_L2TP_KEY_MACHINE_CERTPASS "machine-certpass" #define NM_L2TP_KEY_IPSEC_ENABLE "ipsec-enabled" -#define NM_L2TP_KEY_IPSEC_GATEWAY_ID "ipsec-gateway-id" -#define NM_L2TP_KEY_IPSEC_GROUP_NAME "ipsec-group-name" +#define NM_L2TP_KEY_IPSEC_REMOTE_ID "ipsec-remote-id" +#define NM_L2TP_KEY_IPSEC_GATEWAY_ID "ipsec-gateway-id" /* deprecated, use ipsec-remote-id */ #define NM_L2TP_KEY_IPSEC_PSK "ipsec-psk" #define NM_L2TP_KEY_IPSEC_IKE "ipsec-ike" #define NM_L2TP_KEY_IPSEC_ESP "ipsec-esp" +#define NM_L2TP_KEY_IPSEC_IKELIFETIME "ipsec-ikelifetime" +#define NM_L2TP_KEY_IPSEC_SALIFETIME "ipsec-salifetime" #define NM_L2TP_KEY_IPSEC_FORCEENCAPS "ipsec-forceencaps" +#define NM_L2TP_KEY_IPSEC_IPCOMP "ipsec-ipcomp" +#define NM_L2TP_KEY_IPSEC_IKEV2 "ipsec-ikev2" +#define NM_L2TP_KEY_IPSEC_PFS "ipsec-pfs" + +/* Internal auth-dialog -> service token indicating that no secrets are required + * for the connection if X.509 private keys are used with no password protection. + */ +#define NM_L2TP_KEY_NOSECRET "no-secret" +#define NM_L2TP_AUTHTYPE_PASSWORD "password" +#define NM_L2TP_AUTHTYPE_TLS "tls" +#define NM_L2TP_AUTHTYPE_PSK "psk" #endif /* NM_L2TP_SERVICE_DEFINES_H */