Warning, file /plasma/plasma-nm/vpn/openconnect/openconnectwidget.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2011 Ilia Kats <ilia-kats@gmx.de> 0003 SPDX-FileCopyrightText: 2013 Lukas Tinkl <ltinkl@redhat.com> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "openconnectwidget.h" 0009 #include <QDialog> 0010 #include <QStringList> 0011 #include <QUrl> 0012 0013 #include "ui_openconnectprop.h" 0014 #include "ui_openconnecttoken.h" 0015 0016 #include "nm-openconnect-service.h" 0017 #include <QDialogButtonBox> 0018 #include <QString> 0019 0020 #include <openconnect.h> 0021 0022 #if !OPENCONNECT_CHECK_VER(2, 1) 0023 #define openconnect_has_stoken_support() 0 0024 #endif 0025 #if !OPENCONNECT_CHECK_VER(2, 2) 0026 #define openconnect_has_oath_support() 0 0027 #endif 0028 #if !OPENCONNECT_CHECK_VER(5, 0) 0029 #define openconnect_has_yubioath_support() 0 0030 #endif 0031 0032 using Token = struct { 0033 int tokenIndex; 0034 QString tokenSecret; 0035 }; 0036 0037 class OpenconnectSettingWidgetPrivate 0038 { 0039 public: 0040 Ui_OpenconnectProp ui; 0041 Ui::OpenConnectToken tokenUi; 0042 NetworkManager::VpnSetting::Ptr setting; 0043 QDialog *tokenDlg; 0044 Token token; 0045 }; 0046 0047 OpenconnectSettingWidget::OpenconnectSettingWidget(const NetworkManager::VpnSetting::Ptr &setting, QWidget *parent) 0048 : SettingWidget(setting, parent) 0049 , d_ptr(new OpenconnectSettingWidgetPrivate) 0050 { 0051 Q_D(OpenconnectSettingWidget); 0052 0053 d->ui.setupUi(this); 0054 d->setting = setting; 0055 0056 // Connect for validity check 0057 connect(d->ui.leGateway, &QLineEdit::textChanged, this, &OpenconnectSettingWidget::slotWidgetChanged); 0058 0059 connect(d->ui.buTokens, &QPushButton::clicked, this, &OpenconnectSettingWidget::showTokens); 0060 0061 d->tokenDlg = new QDialog(this); 0062 d->tokenUi.setupUi(d->tokenDlg); 0063 d->tokenUi.leTokenSecret->setPasswordModeEnabled(true); 0064 d->tokenUi.leTokenSecret->setPasswordOptionsEnabled(true); 0065 auto layout = new QVBoxLayout(d->tokenDlg); 0066 layout->addWidget(d->tokenDlg); 0067 d->tokenDlg->setLayout(layout); 0068 connect(d->tokenUi.buttonBox, &QDialogButtonBox::accepted, d->tokenDlg, &QDialog::accept); 0069 connect(d->tokenUi.buttonBox, &QDialogButtonBox::rejected, d->tokenDlg, &QDialog::reject); 0070 connect(d->tokenDlg, &QDialog::rejected, this, &OpenconnectSettingWidget::restoreTokens); 0071 connect(d->tokenDlg, &QDialog::accepted, this, &OpenconnectSettingWidget::saveTokens); 0072 0073 connect(d->tokenUi.cmbTokenMode, 0074 QOverload<int>::of(&QComboBox::currentIndexChanged), 0075 this, 0076 QOverload<int>::of((&OpenconnectSettingWidget::handleTokenSecret))); 0077 0078 // Connect for setting check 0079 watchChangedSetting(); 0080 0081 // Remove these from setting check: 0082 // Just popping up the tokenDlg changes nothing 0083 disconnect(d->ui.buTokens, &QPushButton::clicked, this, &SettingWidget::settingChanged); 0084 // User cancels means nothing should change here 0085 disconnect(d->tokenUi.buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &SettingWidget::settingChanged); 0086 0087 d->tokenUi.gbToken->setVisible(initTokenGroup()); 0088 0089 KAcceleratorManager::manage(this); 0090 0091 if (d->setting) { 0092 loadConfig(d->setting); 0093 } 0094 } 0095 0096 OpenconnectSettingWidget::~OpenconnectSettingWidget() 0097 { 0098 delete d_ptr; 0099 } 0100 0101 void OpenconnectSettingWidget::handleTokenSecret(int index) 0102 { 0103 Q_D(const OpenconnectSettingWidget); 0104 0105 QVariant mode = d->tokenUi.cmbTokenMode->itemData(index); 0106 if (mode == QStringLiteral("disabled")) { 0107 d->tokenUi.leTokenSecret->setEnabled(false); 0108 d->tokenUi.leTokenSecret->setToolTip("No secrets needed."); 0109 } else if (mode == QStringLiteral("stokenrc")) { 0110 d->tokenUi.leTokenSecret->setEnabled(false); 0111 d->tokenUi.leTokenSecret->setToolTip("No secrets needed; will read them from ~/.stokenrc."); 0112 } else if (mode == QStringLiteral("manual")) { 0113 d->tokenUi.leTokenSecret->setToolTip("Insert the secret here. See the openconnect documentation for syntax."); 0114 d->tokenUi.leTokenSecret->setEnabled(true); 0115 } else if (mode == QStringLiteral("totp")) { 0116 d->tokenUi.leTokenSecret->setEnabled(true); 0117 d->tokenUi.leTokenSecret->setToolTip( 0118 "Insert the secret here, with a sha specification and a leading '0x' or 'base32:'. See the openconnect documentation for syntax."); 0119 } else if (mode == QStringLiteral("hotp")) { 0120 d->tokenUi.leTokenSecret->setEnabled(true); 0121 d->tokenUi.leTokenSecret->setToolTip( 0122 "Insert the secret here, with a leading '0x' or 'base32:' and a trailing counter after a comma (','), See the openconnect documentation for " 0123 "syntax."); 0124 } else if (mode == QStringLiteral("yubioath")) { 0125 d->tokenUi.leTokenSecret->setEnabled(true); 0126 d->tokenUi.leTokenSecret->setToolTip("Insert the token Id here, in the form company:username. Make sure to set your Yubikey in CCID mode"); 0127 } else { // Not really needed now, but who knows? 0128 d->tokenUi.leTokenSecret->setEnabled(false); 0129 d->tokenUi.leTokenSecret->setToolTip(""); 0130 } 0131 } 0132 0133 bool OpenconnectSettingWidget::initTokenGroup() 0134 { 0135 Q_D(const OpenconnectSettingWidget); 0136 0137 int validRows = 0; 0138 QStringList tokenLabelList{ 0139 "Disabled", 0140 "RSA SecurID — read from ~/.stokenrc", 0141 "RSA SecurID — manually entered", 0142 "TOTP — manually entered", 0143 "HOTP — manually entered", 0144 "Yubikey", 0145 }; 0146 QStringList tokenModeList{ 0147 "disabled", 0148 "stokenrc", 0149 "manual", 0150 "totp", 0151 "hotp", 0152 "yubioath", 0153 }; 0154 QComboBox *combo = d->tokenUi.cmbTokenMode; 0155 0156 combo->addItem(tokenLabelList[validRows]); 0157 combo->setItemData(validRows, tokenModeList[validRows], Qt::UserRole); 0158 validRows++; 0159 if (openconnect_has_stoken_support()) { 0160 for (; validRows < 3; validRows++) { 0161 combo->addItem(tokenLabelList[validRows]); 0162 combo->setItemData(validRows, tokenModeList[validRows], Qt::UserRole); 0163 } 0164 } 0165 if (openconnect_has_oath_support()) { 0166 combo->addItem(tokenLabelList[validRows]); 0167 combo->setItemData(validRows, tokenModeList[validRows], Qt::UserRole); 0168 validRows++; 0169 if (OPENCONNECT_CHECK_VER(3, 4)) { 0170 combo->addItem(tokenLabelList[validRows]); 0171 combo->setItemData(validRows, tokenModeList[validRows], Qt::UserRole); 0172 validRows++; 0173 } 0174 } 0175 if (openconnect_has_yubioath_support()) { 0176 combo->addItem(tokenLabelList[validRows]); 0177 combo->setItemData(validRows, tokenModeList[validRows], Qt::UserRole); 0178 } 0179 return validRows > 0; 0180 } 0181 0182 void OpenconnectSettingWidget::loadConfig(const NetworkManager::Setting::Ptr &setting) 0183 { 0184 Q_D(OpenconnectSettingWidget); 0185 0186 // General settings 0187 const NMStringMap dataMap = setting.staticCast<NetworkManager::VpnSetting>()->data(); 0188 0189 int cmbProtocolIndex; 0190 // No value corresponds to "anyconnect", matching GNOME and the openconnect binary itself. 0191 if (!dataMap.contains(NM_OPENCONNECT_KEY_PROTOCOL) || dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("anyconnect")) { 0192 cmbProtocolIndex = 0; 0193 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("nc")) { 0194 cmbProtocolIndex = 1; 0195 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("gp")) { 0196 cmbProtocolIndex = 2; 0197 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("pulse")) { 0198 cmbProtocolIndex = 3; // pulse, Pulse Connect Secure 0199 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("f5")) { 0200 cmbProtocolIndex = 4; // F5 BIG-IP SSL VPN 0201 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("fortinet")) { 0202 cmbProtocolIndex = 5; // Fortinet SSL VPN 0203 } else if (dataMap[NM_OPENCONNECT_KEY_PROTOCOL] == QLatin1String("array")) { 0204 cmbProtocolIndex = 6; // Array SSL VPN 0205 } else { 0206 cmbProtocolIndex = 3; // pulse, Pulse Connect Secure is the default 0207 } 0208 0209 int cmbReportedOsIndex; 0210 if (!dataMap.contains(NM_OPENCONNECT_KEY_REPORTED_OS)) { 0211 cmbReportedOsIndex = 0; 0212 } else if (dataMap[NM_OPENCONNECT_KEY_REPORTED_OS] == QLatin1String("linux")) { 0213 cmbReportedOsIndex = 1; 0214 } else if (dataMap[NM_OPENCONNECT_KEY_REPORTED_OS] == QLatin1String("linux-64")) { 0215 cmbReportedOsIndex = 2; 0216 } else if (dataMap[NM_OPENCONNECT_KEY_REPORTED_OS] == QLatin1String("win")) { 0217 cmbReportedOsIndex = 3; 0218 } else if (dataMap[NM_OPENCONNECT_KEY_REPORTED_OS] == QLatin1String("mac-intel")) { 0219 cmbReportedOsIndex = 4; 0220 } else if (dataMap[NM_OPENCONNECT_KEY_REPORTED_OS] == QLatin1String("android")) { 0221 cmbReportedOsIndex = 5; 0222 } else { 0223 cmbReportedOsIndex = 6; // apple-ios 0224 } 0225 0226 d->ui.cmbProtocol->setCurrentIndex(cmbProtocolIndex); 0227 d->ui.leGateway->setText(dataMap[NM_OPENCONNECT_KEY_GATEWAY]); 0228 d->ui.leCaCertificate->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENCONNECT_KEY_CACERT])); 0229 d->ui.leProxy->setText(dataMap[NM_OPENCONNECT_KEY_PROXY]); 0230 d->ui.leUserAgent->setText(dataMap[NM_OPENCONNECT_KEY_USERAGENT]); 0231 d->ui.cmbReportedOs->setCurrentIndex(cmbReportedOsIndex); 0232 d->ui.chkAllowTrojan->setChecked(dataMap[NM_OPENCONNECT_KEY_CSD_ENABLE] == "yes"); 0233 d->ui.leCsdWrapperScript->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENCONNECT_KEY_CSD_WRAPPER])); 0234 d->ui.leUserCert->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENCONNECT_KEY_USERCERT])); 0235 d->ui.leUserPrivateKey->setUrl(QUrl::fromLocalFile(dataMap[NM_OPENCONNECT_KEY_PRIVKEY])); 0236 d->ui.chkUseFsid->setChecked(dataMap[NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID] == "yes"); 0237 d->ui.preventInvalidCert->setChecked(dataMap[NM_OPENCONNECT_KEY_PREVENT_INVALID_CERT] == "yes"); 0238 0239 // Token settings 0240 const NetworkManager::Setting::SecretFlags tokenSecretFlag = 0241 static_cast<NetworkManager::Setting::SecretFlags>(dataMap.value(NM_OPENCONNECT_KEY_TOKEN_SECRET "-flags").toInt()); 0242 if (tokenSecretFlag == NetworkManager::Setting::None) { 0243 d->tokenUi.leTokenSecret->setPasswordOption(PasswordField::StoreForAllUsers); 0244 } else if (tokenSecretFlag == NetworkManager::Setting::AgentOwned) { 0245 d->tokenUi.leTokenSecret->setPasswordOption(PasswordField::StoreForUser); 0246 } else { 0247 d->tokenUi.leTokenSecret->setPasswordOption(PasswordField::AlwaysAsk); 0248 } 0249 for (int index = 0; index < d->tokenUi.cmbTokenMode->count(); index++) { 0250 if (d->tokenUi.cmbTokenMode->itemData(index, Qt::UserRole) == dataMap[NM_OPENCONNECT_KEY_TOKEN_MODE]) { 0251 d->tokenUi.cmbTokenMode->setCurrentIndex(index); 0252 d->token.tokenIndex = index; 0253 if (index > 1) { 0254 loadSecrets(d->setting); 0255 } 0256 break; 0257 } 0258 } 0259 } 0260 0261 void OpenconnectSettingWidget::loadSecrets(const NetworkManager::Setting::Ptr &setting) 0262 { 0263 Q_D(OpenconnectSettingWidget); 0264 0265 NetworkManager::VpnSetting::Ptr vpnSetting = setting.staticCast<NetworkManager::VpnSetting>(); 0266 0267 if (vpnSetting) { 0268 const NMStringMap secrets = vpnSetting->secrets(); 0269 d->tokenUi.leTokenSecret->setText(secrets.value(NM_OPENCONNECT_KEY_TOKEN_SECRET)); 0270 d->token.tokenSecret = secrets.value(NM_OPENCONNECT_KEY_TOKEN_SECRET); 0271 } 0272 } 0273 0274 QVariantMap OpenconnectSettingWidget::setting() const 0275 { 0276 Q_D(const OpenconnectSettingWidget); 0277 0278 NetworkManager::VpnSetting setting; 0279 setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_OPENCONNECT)); 0280 0281 NMStringMap data; 0282 NMStringMap secrets; 0283 QString protocol; 0284 switch (d->ui.cmbProtocol->currentIndex()) { 0285 case 0: 0286 protocol = QLatin1String("anyconnect"); 0287 break; 0288 case 1: 0289 protocol = QLatin1String("nc"); 0290 break; 0291 case 2: 0292 protocol = QLatin1String("gp"); 0293 break; 0294 case 3: 0295 protocol = QLatin1String("pulse"); 0296 break; 0297 case 4: 0298 protocol = QLatin1String("f5"); 0299 break; 0300 case 5: 0301 protocol = QLatin1String("fortinet"); 0302 break; 0303 case 6: 0304 protocol = QLatin1String("array"); 0305 break; 0306 default: 0307 protocol = QLatin1String("pulse"); 0308 } 0309 0310 QString reportedOs; 0311 switch (d->ui.cmbReportedOs->currentIndex()) { 0312 case 0: 0313 reportedOs = QString(); 0314 break; 0315 case 1: 0316 reportedOs = QLatin1String("linux"); 0317 break; 0318 case 2: 0319 reportedOs = QLatin1String("linux-64"); 0320 break; 0321 case 3: 0322 reportedOs = QLatin1String("win"); 0323 break; 0324 case 4: 0325 reportedOs = QLatin1String("mac-intel"); 0326 break; 0327 case 5: 0328 reportedOs = QLatin1String("android"); 0329 break; 0330 default: 0331 reportedOs = QLatin1String("apple-ios"); 0332 } 0333 0334 data.insert(NM_OPENCONNECT_KEY_PROTOCOL, protocol); 0335 data.insert(QLatin1String(NM_OPENCONNECT_KEY_GATEWAY), d->ui.leGateway->text()); 0336 if (d->ui.leCaCertificate->url().isValid()) { 0337 data.insert(QLatin1String(NM_OPENCONNECT_KEY_CACERT), d->ui.leCaCertificate->url().toLocalFile()); 0338 } 0339 if (!d->ui.leProxy->text().isEmpty()) { 0340 data.insert(QLatin1String(NM_OPENCONNECT_KEY_PROXY), d->ui.leProxy->text()); 0341 } 0342 if (!d->ui.leUserAgent->text().isEmpty()) { 0343 data.insert(QLatin1String(NM_OPENCONNECT_KEY_USERAGENT), d->ui.leUserAgent->text()); 0344 } 0345 data.insert(NM_OPENCONNECT_KEY_REPORTED_OS, reportedOs); 0346 data.insert(QLatin1String(NM_OPENCONNECT_KEY_CSD_ENABLE), d->ui.chkAllowTrojan->isChecked() ? "yes" : "no"); 0347 if (d->ui.leCsdWrapperScript->url().isValid()) { 0348 data.insert(QLatin1String(NM_OPENCONNECT_KEY_CSD_WRAPPER), d->ui.leCsdWrapperScript->url().toLocalFile()); 0349 } 0350 if (d->ui.leUserCert->url().isValid()) { 0351 data.insert(QLatin1String(NM_OPENCONNECT_KEY_USERCERT), d->ui.leUserCert->url().toLocalFile()); 0352 } 0353 if (d->ui.leUserPrivateKey->url().isValid()) { 0354 data.insert(QLatin1String(NM_OPENCONNECT_KEY_PRIVKEY), d->ui.leUserPrivateKey->url().toLocalFile()); 0355 } 0356 data.insert(QLatin1String(NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID), d->ui.chkUseFsid->isChecked() ? "yes" : "no"); 0357 data.insert(QLatin1String(NM_OPENCONNECT_KEY_PREVENT_INVALID_CERT), d->ui.preventInvalidCert->isChecked() ? "yes" : "no"); 0358 0359 int index = d->tokenUi.cmbTokenMode->currentIndex(); 0360 data.insert(QLatin1String(NM_OPENCONNECT_KEY_TOKEN_MODE), d->tokenUi.cmbTokenMode->itemData(index, Qt::UserRole).toString()); 0361 secrets.insert(QLatin1String(NM_OPENCONNECT_KEY_TOKEN_SECRET), d->tokenUi.leTokenSecret->text()); 0362 0363 // Restore previous flags, this is necessary for keeping secrets stored in KWallet 0364 for (const QString &key : d->setting->data().keys()) { 0365 if (key.contains(QLatin1String("-flags"))) { 0366 data.insert(key, d->setting->data().value(key)); 0367 } 0368 } 0369 0370 if (d->tokenUi.leTokenSecret->passwordOption() == PasswordField::StoreForAllUsers) { 0371 data.insert(NM_OPENCONNECT_KEY_TOKEN_SECRET "-flags", QString::number(NetworkManager::Setting::None)); 0372 } else if (d->tokenUi.leTokenSecret->passwordOption() == PasswordField::StoreForUser) { 0373 data.insert(NM_OPENCONNECT_KEY_TOKEN_SECRET "-flags", QString::number(NetworkManager::Setting::AgentOwned)); 0374 } else { 0375 data.insert(NM_OPENCONNECT_KEY_TOKEN_SECRET "-flags", QString::number(NetworkManager::Setting::NotSaved)); 0376 } 0377 0378 /* These are different for every login session, and should not be stored */ 0379 data.insert(QLatin1String(NM_OPENCONNECT_KEY_COOKIE "-flags"), QString::number(NetworkManager::Setting::NotSaved)); 0380 data.insert(QLatin1String(NM_OPENCONNECT_KEY_GWCERT "-flags"), QString::number(NetworkManager::Setting::NotSaved)); 0381 data.insert(QLatin1String(NM_OPENCONNECT_KEY_GATEWAY "-flags"), QString::number(NetworkManager::Setting::NotSaved)); 0382 0383 setting.setData(data); 0384 setting.setSecrets(secrets); 0385 0386 return setting.toMap(); 0387 } 0388 0389 void OpenconnectSettingWidget::restoreTokens() 0390 { 0391 Q_D(const OpenconnectSettingWidget); 0392 0393 d->tokenUi.cmbTokenMode->setCurrentIndex(d->token.tokenIndex); 0394 d->tokenUi.leTokenSecret->setText(d->token.tokenSecret); 0395 } 0396 0397 void OpenconnectSettingWidget::saveTokens() 0398 { 0399 Q_D(OpenconnectSettingWidget); 0400 0401 d->token.tokenIndex = d->tokenUi.cmbTokenMode->currentIndex(); 0402 d->token.tokenSecret = d->tokenUi.leTokenSecret->text(); 0403 } 0404 0405 void OpenconnectSettingWidget::showTokens() 0406 { 0407 Q_D(OpenconnectSettingWidget); 0408 0409 d->tokenDlg->show(); 0410 } 0411 0412 bool OpenconnectSettingWidget::isValid() const 0413 { 0414 Q_D(const OpenconnectSettingWidget); 0415 0416 return !d->ui.leGateway->text().isEmpty(); 0417 }