Skip to content

Commit

Permalink
Merge pull request debauchee#803 from p12tic/crypto-cert-fixes
Browse files Browse the repository at this point in the history
Regenerate server certificate when it is broken or has too small key size
  • Loading branch information
p12tic authored Aug 22, 2020
2 parents d186548 + c815abf commit d58a9fb
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 43 deletions.
2 changes: 1 addition & 1 deletion src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ add_executable (barrier WIN32

include_directories (./src)

target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network)
target_link_libraries (barrier Qt5::Core Qt5::Widgets Qt5::Network ${OPENSSL_LIBS})
target_compile_definitions (barrier PRIVATE -DBARRIER_VERSION_STAGE="${BARRIER_VERSION_STAGE}")
target_compile_definitions (barrier PRIVATE -DBARRIER_REVISION="${BARRIER_REVISION}")

Expand Down
4 changes: 4 additions & 0 deletions src/gui/src/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,10 @@ void MainWindow::updateSSLFingerprint()
{
if (m_AppConfig->getCryptoEnabled() && m_pSslCertificate == nullptr) {
m_pSslCertificate = new SslCertificate(this);
connect(m_pSslCertificate, &SslCertificate::info, [&](QString info)
{
appendLogInfo(info);
});
m_pSslCertificate->generateCertificate();
}
if (m_AppConfig->getCryptoEnabled() && Fingerprint::local().fileExists()) {
Expand Down
116 changes: 92 additions & 24 deletions src/gui/src/SslCertificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
#include <QDir>
#include <QCoreApplication>

#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>

static const char kCertificateLifetime[] = "365";
static const char kCertificateSubjectInfo[] = "/CN=Barrier";
static const char kCertificateFilename[] = "Barrier.pem";
Expand All @@ -37,13 +43,13 @@ static const char kConfigFile[] = "barrier.conf";
SslCertificate::SslCertificate(QObject *parent) :
QObject(parent)
{
m_ProfileDir = QString::fromStdString(DataDirectories::profile());
if (m_ProfileDir.isEmpty()) {
m_ProfileDir = DataDirectories::profile();
if (m_ProfileDir.empty()) {
emit error(tr("Failed to get profile directory."));
}
}

bool SslCertificate::runTool(const QStringList& args)
std::pair<bool, std::string> SslCertificate::runTool(const QStringList& args)
{
QString program;
#if defined(Q_OS_WIN)
Expand All @@ -66,11 +72,12 @@ bool SslCertificate::runTool(const QStringList& args)
process.start(program, args);

bool success = process.waitForStarted();
std::string output;

QString standardError;
if (success && process.waitForFinished())
{
m_ToolOutput = process.readAllStandardOutput().trimmed();
output = process.readAllStandardOutput().trimmed().toStdString();
standardError = process.readAllStandardError().trimmed();
}

Expand All @@ -82,26 +89,18 @@ bool SslCertificate::runTool(const QStringList& args)
.arg(program)
.arg(process.exitCode())
.arg(standardError.isEmpty() ? "Unknown" : standardError));
return false;
return {false, output};
}

return true;
return {true, output};
}

void SslCertificate::generateCertificate()
{
QString sslDirPath = QString("%1%2%3")
.arg(m_ProfileDir)
.arg(QDir::separator())
.arg(kSslDir);

QString filename = QString("%1%2%3")
.arg(sslDirPath)
.arg(QDir::separator())
.arg(kCertificateFilename);
auto filename = QString::fromStdString(getCertificatePath());

QFile file(filename);
if (!file.exists()) {
if (!file.exists() || !isCertificateValid(filename)) {
QStringList arguments;

// self signed certificate
Expand All @@ -123,7 +122,7 @@ void SslCertificate::generateCertificate()
arguments.append("-newkey");
arguments.append("rsa:2048");

QDir sslDir(sslDirPath);
QDir sslDir(QString::fromStdString(getCertificateDirectory()));
if (!sslDir.exists()) {
sslDir.mkpath(".");
}
Expand All @@ -136,7 +135,7 @@ void SslCertificate::generateCertificate()
arguments.append("-out");
arguments.append(filename);

if (!runTool(arguments)) {
if (!runTool(arguments).first) {
return;
}

Expand All @@ -158,21 +157,90 @@ void SslCertificate::generateFingerprint(const QString& certificateFilename)
arguments.append("-in");
arguments.append(certificateFilename);

if (!runTool(arguments)) {
auto ret = runTool(arguments);
bool success = ret.first;
std::string output = ret.second;

if (!success) {
return;
}

// find the fingerprint from the tool output
int i = m_ToolOutput.indexOf("=");
if (i != -1) {
auto i = output.find_first_of('=');
if (i != std::string::npos) {
i++;
QString fingerprint = m_ToolOutput.mid(
i, m_ToolOutput.size() - i);
auto fingerprint = output.substr(
i, output.size() - i);

Fingerprint::local().trust(fingerprint, false);
Fingerprint::local().trust(QString::fromStdString(fingerprint), false);
emit info(tr("SSL fingerprint generated."));
}
else {
emit error(tr("Failed to find SSL fingerprint."));
}
}

std::string SslCertificate::getCertificatePath()
{
return getCertificateDirectory() + QDir::separator().toLatin1() + kCertificateFilename;
}

std::string SslCertificate::getCertificateDirectory()
{
return m_ProfileDir + QDir::separator().toLatin1() + kSslDir;
}

bool SslCertificate::isCertificateValid(const QString& path)
{
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();

BIO* bio = BIO_new(BIO_s_file());

auto ret = BIO_read_filename(bio, path.toStdString().c_str());
if (!ret) {
emit info(tr("Could not read from default certificate file."));
BIO_free_all(bio);
return false;
}

X509* cert = PEM_read_bio_X509(bio, NULL, 0, NULL);
if (!cert) {
emit info(tr("Error loading default certificate file to memory."));
BIO_free_all(bio);
return false;
}

EVP_PKEY* pubkey = X509_get_pubkey(cert);
if (!pubkey) {
emit info(tr("Default certificate key file does not contain valid public key"));
X509_free(cert);
BIO_free_all(bio);
return false;
}

auto type = EVP_PKEY_type(EVP_PKEY_id(pubkey));
if (type != EVP_PKEY_RSA && type != EVP_PKEY_DSA) {
emit info(tr("Public key in default certificate key file is not RSA or DSA"));
EVP_PKEY_free(pubkey);
X509_free(cert);
BIO_free_all(bio);
return false;
}

auto bits = EVP_PKEY_bits(pubkey);
if (bits < 2048) {
// We could have small keys in old barrier installations
emit info(tr("Public key in default certificate key file is too small."));
EVP_PKEY_free(pubkey);
X509_free(cert);
BIO_free_all(bio);
return false;
}

EVP_PKEY_free(pubkey);
X509_free(cert);
BIO_free_all(bio);
return true;
}
10 changes: 7 additions & 3 deletions src/gui/src/SslCertificate.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#pragma once

#include <QObject>
#include <string>

class SslCertificate : public QObject
{
Expand All @@ -35,10 +36,13 @@ public slots:
void generateFinished();

private:
bool runTool(const QStringList& args);
std::pair<bool, std::string> runTool(const QStringList& args);
void generateFingerprint(const QString& certificateFilename);

std::string getCertificatePath();
std::string getCertificateDirectory();

bool isCertificateValid(const QString& path);
private:
QString m_ProfileDir;
QString m_ToolOutput;
std::string m_ProfileDir;
};
23 changes: 10 additions & 13 deletions src/lib/net/SecureSocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ SecureSocket::initSsl(bool server)
initContext(server);
}

bool SecureSocket::loadCertificates(std::string& filename)
bool SecureSocket::loadCertificates(const std::string& filename)
{
if (filename.empty()) {
showError("ssl certificate is not specified");
Expand All @@ -341,29 +341,27 @@ bool SecureSocket::loadCertificates(std::string& filename)
file.close();

if (!exist) {
std::string errorMsg("ssl certificate doesn't exist: ");
errorMsg.append(filename);
showError(errorMsg.c_str());
showError("ssl certificate doesn't exist: " + filename);
return false;
}
}

int r = 0;
r = SSL_CTX_use_certificate_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM);
if (r <= 0) {
showError("could not use ssl certificate");
showError("could not use ssl certificate: " + filename);
return false;
}

r = SSL_CTX_use_PrivateKey_file(m_ssl->m_context, filename.c_str(), SSL_FILETYPE_PEM);
if (r <= 0) {
showError("could not use ssl private key");
showError("could not use ssl private key: " + filename);
return false;
}

r = SSL_CTX_check_private_key(m_ssl->m_context);
if (!r) {
showError("could not verify ssl private key");
showError("could not verify ssl private key: " + filename);
return false;
}

Expand Down Expand Up @@ -403,7 +401,7 @@ SecureSocket::initContext(bool server)
SSL_CTX_set_options(m_ssl->m_context, SSL_OP_NO_SSLv3);

if (m_ssl->m_context == NULL) {
showError();
showError("");
}
}

Expand Down Expand Up @@ -618,16 +616,15 @@ SecureSocket::checkResult(int status, int& retry)

if (isFatal()) {
retry = 0;
showError();
showError("");
disconnect();
}
}

void
SecureSocket::showError(const char* reason)
void SecureSocket::showError(const std::string& reason)
{
if (reason != NULL) {
LOG((CLOG_ERR "%s", reason));
if (!reason.empty()) {
LOG((CLOG_ERR "%s", reason.c_str()));
}

std::string error = getError();
Expand Down
4 changes: 2 additions & 2 deletions src/lib/net/SecureSocket.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class SecureSocket : public TCPSocket {
EJobResult doRead() override;
EJobResult doWrite() override;
void initSsl(bool server);
bool loadCertificates(std::string& CertFile);
bool loadCertificates(const std::string& filename);

private:
// SSL
Expand All @@ -65,7 +65,7 @@ class SecureSocket : public TCPSocket {
int secureConnect(int s);
bool showCertificate();
void checkResult(int n, int& retry);
void showError(const char* reason = NULL);
void showError(const std::string& reason);
std::string getError();
void disconnect();
void formatFingerprint(std::string& fingerprint, bool hex = true, bool separator = true);
Expand Down

0 comments on commit d58a9fb

Please sign in to comment.