Skip to content

Commit

Permalink
Added support for SSL Client Authentication.
Browse files Browse the repository at this point in the history
Added configuration switches:
--ssl-client-certificates-file Location of the client certificate file eventually containing a secret key.
--ssl-client-key-file          Location of a file containing just a secret key. Only needed in case of separate locations of certificate and key.
--ssl-client-key-passphrase    Passphrase for the secret key.

Issue: ariya#11275
  • Loading branch information
Florian Fray authored and ariya committed Jul 21, 2015
1 parent beec45d commit e4c0d9f
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 41 deletions.
46 changes: 46 additions & 0 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ static const struct QCommandLineConfigEntry flags[] = {
{ QCommandLine::Option, '\0', "ssl-protocol", "Selects a specific SSL protocol version to offer. Values (case insensitive): TLSv1.2, TLSv1.1, TLSv1.0, TLSv1 (same as v1.0), SSLv3, or ANY. Default is to offer all that Qt thinks are secure (SSLv3 and up). Not all values may be supported, depending on the system OpenSSL library.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-ciphers", "Sets supported TLS/SSL ciphers. Argument is a colon-separated list of OpenSSL cipher names (macros like ALL, kRSA, etc. may not be used). Default matches modern browsers.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-certificates-path", "Sets the location for custom CA certificates (if none set, uses system default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-client-certificate-file", "Sets the location of a client certificate", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-client-key-file", "Sets the location of a clients' private key", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-client-key-passphrase", "Sets the passphrase for the clients' private key", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver", "Starts in 'Remote WebDriver mode' (embedded GhostDriver): '[[<IP>:]<PORT>]' (default '127.0.0.1:8910') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-logfile", "File where to write the WebDriver's Log (default 'none') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-loglevel", "WebDriver Logging Level: (supported: 'ERROR', 'WARN', 'INFO', 'DEBUG') (default 'INFO') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
Expand Down Expand Up @@ -589,6 +592,9 @@ void Config::resetToDefaults()
":RC4-SHA"
":RC4-MD5");
m_sslCertificatesPath.clear();
m_sslClientCertificateFile.clear();
m_sslClientKeyFile.clear();
m_sslClientKeyPassphrase.clear();
m_webdriverIp = QString();
m_webdriverPort = QString();
m_webdriverLogFile = QString();
Expand Down Expand Up @@ -756,6 +762,15 @@ void Config::handleOption(const QString& option, const QVariant& value)
if (option == "ssl-certificates-path") {
setSslCertificatesPath(value.toString());
}
if (option == "ssl-client-certificate-file") {
setSslClientCertificateFile(value.toString());
}
if (option == "ssl-client-key-file") {
setSslClientKeyFile(value.toString());
}
if (option == "ssl-client-key-passphrase") {
setSslClientKeyPassphrase(value.toByteArray());
}
if (option == "webdriver") {
setWebdriver(value.toString().length() > 0 ? value.toString() : DEFAULT_WEBDRIVER_CONFIG);
}
Expand Down Expand Up @@ -825,3 +840,34 @@ void Config::setSslCertificatesPath(const QString& sslCertificatesPath)
m_sslCertificatesPath = sslCertificatesPath;
}
}

QString Config::sslClientCertificateFile() const
{
return m_sslClientCertificateFile;
}

void Config::setSslClientCertificateFile(const QString& sslClientCertificateFile)
{
m_sslClientCertificateFile = sslClientCertificateFile;
}

QString Config::sslClientKeyFile() const
{
return m_sslClientKeyFile;
}

void Config::setSslClientKeyFile(const QString& sslClientKeyFile)
{
m_sslClientKeyFile = sslClientKeyFile;
}

QByteArray Config::sslClientKeyPassphrase() const
{
return m_sslClientKeyPassphrase;
}

void Config::setSslClientKeyPassphrase(const QByteArray& sslClientKeyPassphrase)
{
m_sslClientKeyPassphrase = sslClientKeyPassphrase;
}

15 changes: 15 additions & 0 deletions src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class Config: public QObject
Q_PROPERTY(QString sslProtocol READ sslProtocol WRITE setSslProtocol)
Q_PROPERTY(QString sslCiphers READ sslCiphers WRITE setSslCiphers)
Q_PROPERTY(QString sslCertificatesPath READ sslCertificatesPath WRITE setSslCertificatesPath)
Q_PROPERTY(QString sslClientCertificateFile READ sslClientCertificateFile WRITE setSslClientCertificateFile)
Q_PROPERTY(QString sslClientKeyFile READ sslClientKeyFile WRITE setSslClientKeyFile)
Q_PROPERTY(QByteArray sslClientKeyPassphrase READ sslClientKeyPassphrase WRITE setSslClientKeyPassphrase)
Q_PROPERTY(QString webdriver READ webdriver WRITE setWebdriver)
Q_PROPERTY(QString webdriverLogFile READ webdriverLogFile WRITE setWebdriverLogFile)
Q_PROPERTY(QString webdriverLogLevel READ webdriverLogLevel WRITE setWebdriverLogLevel)
Expand Down Expand Up @@ -171,6 +174,15 @@ class Config: public QObject
void setSslCertificatesPath(const QString& sslCertificatesPath);
QString sslCertificatesPath() const;

void setSslClientCertificateFile(const QString& sslClientCertificateFile);
QString sslClientCertificateFile() const;

void setSslClientKeyFile(const QString& sslClientKeyFile);
QString sslClientKeyFile() const;

void setSslClientKeyPassphrase(const QByteArray& sslClientKeyPassphrase);
QByteArray sslClientKeyPassphrase() const;

void setWebdriver(const QString& webdriverConfig);
QString webdriver() const;
bool isWebdriverMode() const;
Expand Down Expand Up @@ -232,6 +244,9 @@ public slots:
QString m_sslProtocol;
QString m_sslCiphers;
QString m_sslCertificatesPath;
QString m_sslClientCertificateFile;
QString m_sslClientKeyFile;
QByteArray m_sslClientKeyPassphrase;
QString m_webdriverIp;
QString m_webdriverPort;
QString m_webdriverLogFile;
Expand Down
116 changes: 75 additions & 41 deletions src/networkaccessmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include <QSslSocket>
#include <QSslCertificate>
#include <QSslCipher>
#include <QSslKey>
#include <QRegExp>

#include "phantom.h"
Expand Down Expand Up @@ -174,58 +175,92 @@ NetworkAccessManager::NetworkAccessManager(QObject* parent, const Config* config
}

if (QSslSocket::supportsSsl()) {
m_sslConfiguration = QSslConfiguration::defaultConfiguration();
prepareSslConfiguration(config);
}


connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*, QAuthenticator*)));

connect(&m_replyTracker, SIGNAL(started(QNetworkReply*, int)), this, SLOT(handleStarted(QNetworkReply*, int)));
connect(&m_replyTracker, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(handleSslErrors(QNetworkReply*, const QList<QSslError>&)));
connect(&m_replyTracker, SIGNAL(error(QNetworkReply*, int, QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply*, int)));
connect(&m_replyTracker, SIGNAL(finished(QNetworkReply*, int, int, const QString&, const QString&)), SLOT(handleFinished(QNetworkReply*, int, int, const QString&, const QString&)));
}

void NetworkAccessManager::prepareSslConfiguration(const Config* config)
{
m_sslConfiguration = QSslConfiguration::defaultConfiguration();

if (config->ignoreSslErrors()) {
m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
}

if (config->ignoreSslErrors()) {
m_sslConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
bool setProtocol = false;
for (const ssl_protocol_option* proto_opt = ssl_protocol_options;
proto_opt->name;
proto_opt++) {
if (config->sslProtocol() == proto_opt->name) {
m_sslConfiguration.setProtocol(proto_opt->proto);
setProtocol = true;
break;
}
}
// FIXME: actually object to an invalid setting.
if (!setProtocol) {
m_sslConfiguration.setProtocol(QSsl::SecureProtocols);
}

bool setProtocol = false;
for (const ssl_protocol_option* proto_opt = ssl_protocol_options;
proto_opt->name;
proto_opt++) {
if (config->sslProtocol() == proto_opt->name) {
m_sslConfiguration.setProtocol(proto_opt->proto);
setProtocol = true;
break;
// Essentially the same as what QSslSocket::setCiphers(QString) does.
// That overload isn't available on QSslConfiguration.
if (!config->sslCiphers().isEmpty()) {
QList<QSslCipher> cipherList;
foreach(const QString & cipherName,
config->sslCiphers().split(QLatin1String(":"),
QString::SkipEmptyParts)) {
QSslCipher cipher(cipherName);
if (!cipher.isNull()) {
cipherList << cipher;
}
}
// FIXME: actually object to an invalid setting.
if (!setProtocol) {
m_sslConfiguration.setProtocol(QSsl::SecureProtocols);
if (!cipherList.isEmpty()) {
m_sslConfiguration.setCiphers(cipherList);
}
}

// Essentially the same as what QSslSocket::setCiphers(QString) does.
// That overload isn't available on QSslConfiguration.
if (!config->sslCiphers().isEmpty()) {
QList<QSslCipher> cipherList;
foreach(const QString & cipherName,
config->sslCiphers().split(QLatin1String(":"),
QString::SkipEmptyParts)) {
QSslCipher cipher(cipherName);
if (!cipher.isNull()) {
cipherList << cipher;
}
}
if (!cipherList.isEmpty()) {
m_sslConfiguration.setCiphers(cipherList);
}
}
if (!config->sslCertificatesPath().isEmpty()) {
QList<QSslCertificate> caCerts = QSslCertificate::fromPath(
config->sslCertificatesPath(), QSsl::Pem, QRegExp::Wildcard);

m_sslConfiguration.setCaCertificates(caCerts);
}

if (!config->sslClientCertificateFile().isEmpty()) {
QList<QSslCertificate> clientCerts = QSslCertificate::fromPath(
config->sslClientCertificateFile(), QSsl::Pem, QRegExp::Wildcard);

if (!config->sslCertificatesPath().isEmpty()) {
QList<QSslCertificate> caCerts = QSslCertificate::fromPath(
config->sslCertificatesPath(), QSsl::Pem, QRegExp::Wildcard);
if (!clientCerts.isEmpty()) {
QSslCertificate clientCert = clientCerts.first();

QList<QSslCertificate> caCerts = m_sslConfiguration.caCertificates();
caCerts.append(clientCert);
m_sslConfiguration.setCaCertificates(caCerts);
}
}
m_sslConfiguration.setLocalCertificate(clientCert);

connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), SLOT(provideAuthentication(QNetworkReply*, QAuthenticator*)));
QFile* keyFile = NULL;
if (config->sslClientKeyFile().isEmpty()) {
keyFile = new QFile(config->sslClientCertificateFile());
} else {
keyFile = new QFile(config->sslClientKeyFile());
}

connect(&m_replyTracker, SIGNAL(started(QNetworkReply*, int)), this, SLOT(handleStarted(QNetworkReply*, int)));
connect(&m_replyTracker, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(handleSslErrors(QNetworkReply*, const QList<QSslError>&)));
connect(&m_replyTracker, SIGNAL(error(QNetworkReply*, int, QNetworkReply::NetworkError)), this, SLOT(handleNetworkError(QNetworkReply*, int)));
connect(&m_replyTracker, SIGNAL(finished(QNetworkReply*, int, int, const QString&, const QString&)), SLOT(handleFinished(QNetworkReply*, int, int, const QString&, const QString&)));
if (keyFile->open(QIODevice::ReadOnly | QIODevice::Text)) {
QSslKey key(keyFile->readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, config->sslClientKeyPassphrase());

m_sslConfiguration.setPrivateKey(key);
keyFile->close();
}
}
}
}

void NetworkAccessManager::setUserName(const QString& userName)
Expand Down Expand Up @@ -296,7 +331,6 @@ QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkR
{
QNetworkRequest req(request);
QString scheme = req.url().scheme().toLower();
bool isLocalFile = req.url().isLocalFile();

if (!QSslSocket::supportsSsl()) {
if (scheme == QLatin1String("https")) {
Expand Down
1 change: 1 addition & 0 deletions src/networkaccessmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ private slots:

bool shouldCaptureResponse(const QString& url);
void compileCaptureContentPatterns();
void prepareSslConfiguration(const Config* config);

int m_idCounter;
QNetworkDiskCache* m_networkDiskCache;
Expand Down

0 comments on commit e4c0d9f

Please sign in to comment.