mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2025-03-01 10:33:28 +00:00
Provide more details on pinned certificate files
This commit is contained in:
parent
733aa662bf
commit
a8e9fe30fe
@ -17,7 +17,6 @@
|
|||||||
#include <QSslSocket>
|
#include <QSslSocket>
|
||||||
|
|
||||||
//std includes
|
//std includes
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
@ -231,7 +230,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
|||||||
_networkManager->setTransferTimeout(_requestTimeout.count());
|
_networkManager->setTransferTimeout(_requestTimeout.count());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QDateTime start = QDateTime::currentDateTime();
|
QDateTime const start = QDateTime::currentDateTime();
|
||||||
QString opCode;
|
QString opCode;
|
||||||
QNetworkReply* reply;
|
QNetworkReply* reply;
|
||||||
|
|
||||||
@ -267,7 +266,7 @@ httpResponse ProviderRestApi::executeOperation(QNetworkAccessManager::Operation
|
|||||||
|
|
||||||
// Go into the loop until the request is finished.
|
// Go into the loop until the request is finished.
|
||||||
loop.exec();
|
loop.exec();
|
||||||
QDateTime end = QDateTime::currentDateTime();
|
QDateTime const end = QDateTime::currentDateTime();
|
||||||
|
|
||||||
httpResponse response = (reply->operation() == operation) ? getResponse(reply) : httpResponse();
|
httpResponse response = (reply->operation() == operation) ? getResponse(reply) : httpResponse();
|
||||||
|
|
||||||
@ -283,18 +282,18 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
|||||||
{
|
{
|
||||||
httpResponse response;
|
httpResponse response;
|
||||||
|
|
||||||
HttpStatusCode httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
HttpStatusCode const httpStatusCode = static_cast<HttpStatusCode>(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt());
|
||||||
response.setHttpStatusCode(httpStatusCode);
|
response.setHttpStatusCode(httpStatusCode);
|
||||||
response.setNetworkReplyError(reply->error());
|
response.setNetworkReplyError(reply->error());
|
||||||
response.setHeaders(reply->rawHeaderPairs());
|
response.setHeaders(reply->rawHeaderPairs());
|
||||||
|
|
||||||
if (reply->error() == QNetworkReply::NoError)
|
if (reply->error() == QNetworkReply::NoError)
|
||||||
{
|
{
|
||||||
QByteArray replyData = reply->readAll();
|
QByteArray const replyData = reply->readAll();
|
||||||
if (!replyData.isEmpty())
|
if (!replyData.isEmpty())
|
||||||
{
|
{
|
||||||
QJsonParseError error;
|
QJsonParseError error;
|
||||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(replyData, &error);
|
QJsonDocument const jsonDoc = QJsonDocument::fromJson(replyData, &error);
|
||||||
|
|
||||||
if (error.error != QJsonParseError::NoError)
|
if (error.error != QJsonParseError::NoError)
|
||||||
{
|
{
|
||||||
@ -322,7 +321,7 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (httpStatusCode > 0) {
|
if (httpStatusCode > 0) {
|
||||||
QString httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
QString const httpReason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||||
QString advise;
|
QString advise;
|
||||||
switch ( httpStatusCode ) {
|
switch ( httpStatusCode ) {
|
||||||
case HttpStatusCode::BadRequest:
|
case HttpStatusCode::BadRequest:
|
||||||
@ -339,7 +338,7 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
|||||||
break;
|
break;
|
||||||
case HttpStatusCode::TooManyRequests:
|
case HttpStatusCode::TooManyRequests:
|
||||||
{
|
{
|
||||||
QString retryAfterTime = response.getHeader("Retry-After");
|
QString const retryAfterTime = response.getHeader("Retry-After");
|
||||||
if (!retryAfterTime.isEmpty())
|
if (!retryAfterTime.isEmpty())
|
||||||
{
|
{
|
||||||
advise = "Retry-After: " + response.getHeader("Retry-After");
|
advise = "Retry-After: " + response.getHeader("Retry-After");
|
||||||
@ -366,7 +365,7 @@ httpResponse ProviderRestApi::getResponse(QNetworkReply* const& reply)
|
|||||||
|
|
||||||
void ProviderRestApi::setHeader(QNetworkRequest::KnownHeaders header, const QVariant& value)
|
void ProviderRestApi::setHeader(QNetworkRequest::KnownHeaders header, const QVariant& value)
|
||||||
{
|
{
|
||||||
QVariant headerValue = _networkRequestHeaders.header(header);
|
QVariant const headerValue = _networkRequestHeaders.header(header);
|
||||||
if (headerValue.isNull())
|
if (headerValue.isNull())
|
||||||
{
|
{
|
||||||
_networkRequestHeaders.setHeader(header, value);
|
_networkRequestHeaders.setHeader(header, value);
|
||||||
@ -394,7 +393,7 @@ void httpResponse::setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray httpResponse::getHeader(const QByteArray header) const
|
QByteArray httpResponse::getHeader(const QByteArray& header) const
|
||||||
{
|
{
|
||||||
return _responseHeaders.value(header);
|
return _responseHeaders.value(header);
|
||||||
}
|
}
|
||||||
@ -412,7 +411,7 @@ bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSslCertificate cert (&caFile);
|
QSslCertificate const cert (&caFile);
|
||||||
caFile.close();
|
caFile.close();
|
||||||
|
|
||||||
QList<QSslCertificate> allowedCAs;
|
QList<QSslCertificate> allowedCAs;
|
||||||
@ -424,8 +423,8 @@ bool ProviderRestApi::setCaCertificate(const QString& caFileName)
|
|||||||
#ifndef QT_NO_SSL
|
#ifndef QT_NO_SSL
|
||||||
if (QSslSocket::supportsSsl())
|
if (QSslSocket::supportsSsl())
|
||||||
{
|
{
|
||||||
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors, Qt::UniqueConnection );
|
QObject::connect( _networkManager, &QNetworkAccessManager::sslErrors, this, &ProviderRestApi::onSslErrors );
|
||||||
_networkManager->connectToHostEncrypted(_apiUrl.host(), _apiUrl.port(), configuration);
|
_networkManager->connectToHostEncrypted(_apiUrl.host(), static_cast<quint16>(_apiUrl.port()), configuration);
|
||||||
rc = true;
|
rc = true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -453,8 +452,8 @@ bool ProviderRestApi::checkServerIdentity(const QSslConfiguration& sslConfig) co
|
|||||||
bool isServerIdentified {false};
|
bool isServerIdentified {false};
|
||||||
|
|
||||||
// Perform common name validation
|
// Perform common name validation
|
||||||
QSslCertificate serverCertificate = sslConfig.peerCertificate();
|
QSslCertificate const serverCertificate = sslConfig.peerCertificate();
|
||||||
QStringList commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
QStringList const commonName = serverCertificate.subjectInfo(QSslCertificate::CommonName);
|
||||||
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
if ( commonName.contains(getAlternateServerIdentity(), Qt::CaseInsensitive) )
|
||||||
{
|
{
|
||||||
isServerIdentified = true;
|
isServerIdentified = true;
|
||||||
@ -467,27 +466,36 @@ bool ProviderRestApi::matchesPinnedCertificate(const QSslCertificate& certificat
|
|||||||
{
|
{
|
||||||
bool isMatching {false};
|
bool isMatching {false};
|
||||||
|
|
||||||
QList certificateInfos = certificate.subjectInfo(QSslCertificate::CommonName);
|
QList const certificateInfos = certificate.subjectInfo(QSslCertificate::CommonName);
|
||||||
|
|
||||||
if (certificateInfos.isEmpty())
|
if (certificateInfos.isEmpty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
QString identifier = certificateInfos.constFirst();
|
QString const& identifier = certificateInfos.constFirst();
|
||||||
|
QString const appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
|
QString const certDir = appDataDir + "/certificates";
|
||||||
|
|
||||||
QString appDataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
Debug (_log,"Directory used for pinned certificates: %s", QSTRING_CSTR(certDir));
|
||||||
QString certDir = appDataDir + "/certificates";
|
|
||||||
QDir().mkpath(certDir);
|
|
||||||
|
|
||||||
QString filePath(certDir + "/" + identifier + ".pem");
|
bool const isMkPath = QDir().mkpath(certDir);
|
||||||
|
if (!isMkPath)
|
||||||
|
{
|
||||||
|
Error (_log,"Failed to create directory \"%s\" to store pinned certificates. 'Trust on first use' is rejected.", QSTRING_CSTR(certDir));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString const fileName(identifier + ".pem");
|
||||||
|
QString const filePath(certDir + "/" + fileName);
|
||||||
QFile file(filePath);
|
QFile file(filePath);
|
||||||
|
|
||||||
if (file.open(QIODevice::ReadOnly))
|
if (file.open(QIODevice::ReadOnly))
|
||||||
{
|
{
|
||||||
QList certificates = QSslCertificate::fromDevice(&file, QSsl::Pem);
|
QList const certificates = QSslCertificate::fromDevice(&file, QSsl::Pem);
|
||||||
if (!certificates.isEmpty())
|
if (!certificates.isEmpty())
|
||||||
{
|
{
|
||||||
Debug (_log,"First used certificate loaded successfully");
|
Debug (_log,"First used certificate \"%s\" loaded successfully", QSTRING_CSTR(fileName));
|
||||||
QSslCertificate pinnedeCertificate = certificates.constFirst();
|
QSslCertificate const& pinnedeCertificate = certificates.constFirst();
|
||||||
if (pinnedeCertificate == certificate)
|
if (pinnedeCertificate == certificate)
|
||||||
{
|
{
|
||||||
isMatching = true;
|
isMatching = true;
|
||||||
@ -503,8 +511,8 @@ bool ProviderRestApi::matchesPinnedCertificate(const QSslCertificate& certificat
|
|||||||
{
|
{
|
||||||
if (file.open(QIODevice::WriteOnly))
|
if (file.open(QIODevice::WriteOnly))
|
||||||
{
|
{
|
||||||
QByteArray pemData = certificate.toPem();
|
QByteArray const pemData = certificate.toPem();
|
||||||
qint64 bytesWritten = file.write(pemData);
|
qint64 const bytesWritten = file.write(pemData);
|
||||||
if (bytesWritten == pemData.size())
|
if (bytesWritten == pemData.size())
|
||||||
{
|
{
|
||||||
Debug (_log,"First used certificate saved to file: %s", QSTRING_CSTR(filePath));
|
Debug (_log,"First used certificate saved to file: %s", QSTRING_CSTR(filePath));
|
||||||
@ -538,7 +546,7 @@ void ProviderRestApi::onSslErrors(QNetworkReply* reply, const QList<QSslError>&
|
|||||||
if (_isSeflSignedCertificateAccpeted)
|
if (_isSeflSignedCertificateAccpeted)
|
||||||
{
|
{
|
||||||
// Get the peer certificate associated with the error
|
// Get the peer certificate associated with the error
|
||||||
QSslCertificate certificate = error.certificate();
|
QSslCertificate const certificate = error.certificate();
|
||||||
if (matchesPinnedCertificate(certificate))
|
if (matchesPinnedCertificate(certificate))
|
||||||
{
|
{
|
||||||
Debug (_log,"'Trust on first use' - Certificate received matches pinned certificate");
|
Debug (_log,"'Trust on first use' - Certificate received matches pinned certificate");
|
||||||
|
@ -89,7 +89,7 @@ public:
|
|||||||
void setBody(const QJsonDocument& body) { _responseBody = body; }
|
void setBody(const QJsonDocument& body) { _responseBody = body; }
|
||||||
|
|
||||||
|
|
||||||
QByteArray getHeader(const QByteArray header) const;
|
QByteArray getHeader(const QByteArray& header) const;
|
||||||
void setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs);
|
void setHeaders(const QList<QNetworkReply::RawHeaderPair>& pairs);
|
||||||
|
|
||||||
QString getErrorReason() const { return _errorReason; }
|
QString getErrorReason() const { return _errorReason; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user