From cf3b60ea29526417a35734a4ae4f5a7b0d5560d5 Mon Sep 17 00:00:00 2001 From: "Felix (xq) Queißner" Date: Sat, 20 Jun 2020 00:42:46 +0200 Subject: Adds new feature: Auto-enable and host matching for client certificates --- src/browsertab.cpp | 83 ++++++++++++++++++++++++++++++++++++++-------- src/browsertab.hpp | 1 + src/cryptoidentity.cpp | 29 ++++++++++++++++ src/cryptoidentity.hpp | 6 ++++ src/identitycollection.cpp | 15 +++++++++ src/identitycollection.hpp | 3 ++ 6 files changed, 123 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/browsertab.cpp b/src/browsertab.cpp index 3d16e08..0fe0af6 100644 --- a/src/browsertab.cpp +++ b/src/browsertab.cpp @@ -767,18 +767,7 @@ bool BrowserTab::trySetClientCertificate(const QString &query) return false; } - this->current_identity = dialog.identity(); - - if (not current_identity.isValid()) - { - QMessageBox::warning(this, "Kristall", "Failed to generate temporary crypto-identitiy"); - this->disableClientCertificate(); - return false; - } - - this->ui->enable_client_cert_button->setChecked(true); - - return true; + return this->enableClientCertificate(dialog.identity()); } void BrowserTab::resetClientCertificate() @@ -826,7 +815,7 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o auto answer = QMessageBox::question( this, "Kristall", - QString("You requested a %1-URL with a client certificate, but these are not supported for this scheme. Continue?").arg(url.scheme()) + tr("You requested a %1-URL with a client certificate, but these are not supported for this scheme. Continue?").arg(url.scheme()) ); if(answer != QMessageBox::Yes) return false; @@ -840,7 +829,22 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o auto answer = QMessageBox::question( this, "Kristall", - "You want to visit a new host, but have a client certificate enabled. This may be a risk to expose your identity to another host.\r\nDo you want to keep the certificate enabled?", + tr("You want to visit a new host, but have a client certificate enabled. This may be a risk to expose your identity to another host.\r\nDo you want to keep the certificate enabled?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No + ); + if(answer != QMessageBox::Yes) { + this->disableClientCertificate(); + } + } + + if(this->current_identity.isValid() and this->current_identity.isHostFiltered(url)) { + auto answer = QMessageBox::question( + this, + "Kristall", + tr("Your client certificate has a host filter enabled and this site does not match the host filter.\r\nNew URL: %1\r\nHost Filter: %2\r\nDo you want to keep the certificate enabled?") + .arg(url.toString(QUrl::FullyEncoded)) + .arg(this->current_identity.host_filter), QMessageBox::Yes | QMessageBox::No, QMessageBox::No ); @@ -848,6 +852,44 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o this->disableClientCertificate(); } } + else if(not this->current_identity.isValid()) { + for(auto ident_ptr : global_identities.allIdentities()) + { + if(ident_ptr->isAutomaticallyEnabledOn(url)) { + + auto answer = QMessageBox::question( + this, + "Kristall", + tr("An automatic client certificate was detected for this site:\r\n%1\r\nDo you want to enable that certificate?") + .arg(ident_ptr->display_name), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No + ); + if(answer != QMessageBox::Yes) { + break; + } + + enableClientCertificate(*ident_ptr); + + break; + } + } + } + + if(this->current_identity.isValid()) { + if(not this->current_handler->enableClientCertificate(this->current_identity)) { + auto answer = QMessageBox::question( + this, + "Kristall", + tr("You requested a %1-URL with a client certificate, but these are not supported for this scheme. Continue?").arg(url.scheme()) + ); + if(answer != QMessageBox::Yes) + return false; + this->disableClientCertificate(); + } + } else { + this->disableClientCertificate(); + } this->is_internal_location = (url.scheme() == "about"); this->current_location = url; @@ -858,6 +900,19 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o return this->current_handler->startRequest(url, options); } +bool BrowserTab::enableClientCertificate(const CryptoIdentity &ident) +{ + if (not ident.isValid()) + { + QMessageBox::warning(this, "Kristall", "Failed to generate temporary crypto-identitiy"); + this->disableClientCertificate(); + return false; + } + this->current_identity = ident; + this->ui->enable_client_cert_button->setChecked(true); + return true; +} + void BrowserTab::disableClientCertificate() { for(auto & handler : this->protocol_handlers) { diff --git a/src/browsertab.hpp b/src/browsertab.hpp index 7429fa2..e491f9e 100644 --- a/src/browsertab.hpp +++ b/src/browsertab.hpp @@ -129,6 +129,7 @@ private: bool startRequest(QUrl const & url, ProtocolHandler::RequestOptions options); + bool enableClientCertificate(CryptoIdentity const & ident); void disableClientCertificate(); public: diff --git a/src/cryptoidentity.cpp b/src/cryptoidentity.cpp index 921a422..2d9db59 100644 --- a/src/cryptoidentity.cpp +++ b/src/cryptoidentity.cpp @@ -1,2 +1,31 @@ #include "cryptoidentity.hpp" +#include +#include +#include + +bool CryptoIdentity::isHostFiltered(const QUrl &url) const +{ + if(this->host_filter.isEmpty()) + return false; + + QString url_text = url.toString(QUrl::FullyEncoded); + + QRegExp pattern { this->host_filter, Qt::CaseInsensitive, QRegExp::Wildcard }; + + return not pattern.exactMatch(url_text); +} + +bool CryptoIdentity::isAutomaticallyEnabledOn(const QUrl &url) const +{ + if(this->host_filter.isEmpty()) + return false; + if(not this->auto_enable) + return false; + + QString url_text = url.toString(QUrl::FullyEncoded); + + QRegExp pattern { this->host_filter, Qt::CaseInsensitive, QRegExp::Wildcard }; + + return pattern.exactMatch(url_text); +} diff --git a/src/cryptoidentity.hpp b/src/cryptoidentity.hpp index b693427..9c35cd0 100644 --- a/src/cryptoidentity.hpp +++ b/src/cryptoidentity.hpp @@ -35,6 +35,12 @@ struct CryptoIdentity bool isValid() const { return (not this->certificate.isNull()) and (not this->private_key.isNull()); } + + //! returns true if a host does not match the filter criterion + bool isHostFiltered(QUrl const & url) const; + + //! returns true when the identity should be enabled on url + bool isAutomaticallyEnabledOn(QUrl const & url) const; }; #endif // CRYPTOIDENTITIY_HPP diff --git a/src/identitycollection.cpp b/src/identitycollection.cpp index 2590cdf..1a39e76 100644 --- a/src/identitycollection.cpp +++ b/src/identitycollection.cpp @@ -244,6 +244,21 @@ bool IdentityCollection::deleteGroup(const QString &group_name) return false; } +QVector IdentityCollection::allIdentities() const +{ + QVector identities; + + for(auto const & group : this->root.children) + { + for(auto const & ident : group->children) + { + identities.append(&ident->as().identity); + } + } + + return identities; +} + QModelIndex IdentityCollection::index(int row, int column, const QModelIndex &parent) const { if (not hasIndex(row, column, parent)) diff --git a/src/identitycollection.hpp b/src/identitycollection.hpp index eecf3a4..7db879e 100644 --- a/src/identitycollection.hpp +++ b/src/identitycollection.hpp @@ -66,6 +66,9 @@ public: bool canDeleteGroup(QString const & group_name); bool deleteGroup(QString const & group_name); + //! Returns a list of non-mutable references to all contained identities + QVector allIdentities() const; + public: // Header: // QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; -- cgit v1.2.3