Adds new feature: Auto-enable and host matching for client certificates
This commit is contained in:
parent
bdfd6ba687
commit
cf3b60ea29
|
@ -5,10 +5,9 @@ This document contains TODO items for planned Kristall releases as well as some
|
|||
## 0.3 - TLS and security
|
||||
- [ ] TLS Handling
|
||||
- [ ] Add management for client certificates
|
||||
- [ ] Rename/delete/merge groups
|
||||
- [ ] Allow merge/delete/merge groups
|
||||
- [ ] Import/export PEM certificates and keys
|
||||
- [ ] Add a "scope" option to certificates so users can restrict the scope where the certificate is valid
|
||||
|
||||
|
||||
## 0.4 - The colorful release
|
||||
- [ ] Implement dual-colored icon theme
|
||||
- [ ] Improve UX
|
||||
|
|
|
@ -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,7 @@ 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
|
||||
);
|
||||
|
@ -849,6 +838,59 @@ bool BrowserTab::startRequest(const QUrl &url, ProtocolHandler::RequestOptions o
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
if(answer != QMessageBox::Yes) {
|
||||
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;
|
||||
this->ui->url_bar->setText(url.toString(QUrl::FormattingOptions(QUrl::FullyEncoded)));
|
||||
|
@ -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) {
|
||||
|
|
|
@ -129,6 +129,7 @@ private:
|
|||
|
||||
bool startRequest(QUrl const & url, ProtocolHandler::RequestOptions options);
|
||||
|
||||
bool enableClientCertificate(CryptoIdentity const & ident);
|
||||
void disableClientCertificate();
|
||||
public:
|
||||
|
||||
|
|
|
@ -1,2 +1,31 @@
|
|||
#include "cryptoidentity.hpp"
|
||||
|
||||
#include <QUrl>
|
||||
#include <QRegExp>
|
||||
#include <cassert>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -244,6 +244,21 @@ bool IdentityCollection::deleteGroup(const QString &group_name)
|
|||
return false;
|
||||
}
|
||||
|
||||
QVector<const CryptoIdentity *> IdentityCollection::allIdentities() const
|
||||
{
|
||||
QVector<const CryptoIdentity *> identities;
|
||||
|
||||
for(auto const & group : this->root.children)
|
||||
{
|
||||
for(auto const & ident : group->children)
|
||||
{
|
||||
identities.append(&ident->as<IdentityNode>().identity);
|
||||
}
|
||||
}
|
||||
|
||||
return identities;
|
||||
}
|
||||
|
||||
QModelIndex IdentityCollection::index(int row, int column, const QModelIndex &parent) const
|
||||
{
|
||||
if (not hasIndex(row, column, parent))
|
||||
|
|
|
@ -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<CryptoIdentity const *> allIdentities() const;
|
||||
|
||||
public:
|
||||
// Header:
|
||||
// QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||
|
|
Loading…
Reference in New Issue