From 6742783dd03afc43c8258a5b207693287a2aa4a2 Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Sat, 17 Oct 2009 17:38:05 +0100 Subject: [PATCH 1/2] backport ssl cert chaining --- src/murmur/Cert.cpp | 130 ++++++++++++++++++++++++++++-------------------- src/murmur/Meta.cpp | 88 +++++++++++++++++++++------------ src/murmur/Server.cpp | 1 + src/murmur/Server.h | 2 + 4 files changed, 136 insertions(+), 85 deletions(-) diff --git a/src/murmur/Cert.cpp b/src/murmur/Cert.cpp index d2840c9..22ef028 100644 --- a/src/murmur/Cert.cpp +++ b/src/murmur/Cert.cpp @@ -31,7 +31,9 @@ #include "Server.h" #include "Meta.h" -int add_ext(X509 * crt, int nid, char *value) { +#define SSL_STRING(x) QString::fromLatin1(x).toUtf8().data() + +static int add_ext(X509 * crt, int nid, char *value) { X509_EXTENSION *ex; X509V3_CTX ctx; X509V3_set_ctx_nodb(&ctx); @@ -45,51 +47,85 @@ int add_ext(X509 * crt, int nid, char *value) { return 1; } -void Server::initializeCert() { - QByteArray crt, key, pass; +bool Server::isKeyForCert(const QSslKey &key, const QSslCertificate &cert) { + if (key.isNull() || cert.isNull() || (key.type() != QSsl::PrivateKey)) + return false; + + QByteArray qbaKey = key.toDer(); + QByteArray qbaCert = cert.toDer(); + + X509 *x509 = NULL; + EVP_PKEY *pkey = NULL; + BIO *mem = NULL; + + mem = BIO_new_mem_buf(qbaKey.data(), qbaKey.size()); + BIO_set_close(mem, BIO_NOCLOSE); + pkey = d2i_PrivateKey_bio(mem, NULL); + BIO_free(mem); + + mem = BIO_new_mem_buf(qbaCert.data(), qbaCert.size()); + BIO_set_close(mem, BIO_NOCLOSE); + x509 = d2i_X509_bio(mem, NULL); + BIO_free(mem); + mem = NULL; - if (! QSslSocket::supportsSsl()) { - qFatal("Qt without SSL Support"); + if (x509 && pkey && X509_check_private_key(x509, pkey)) { + EVP_PKEY_free(pkey); + X509_free(x509); + return true; } + if (pkey) + EVP_PKEY_free(pkey); + if (x509) + X509_free(x509); + return false; +} + +void Server::initializeCert() { + QByteArray crt, key, pass; + crt = getConf("certificate", QString()).toByteArray(); key = getConf("key", QString()).toByteArray(); pass = getConf("passphrase", QByteArray()).toByteArray(); - if (! crt.isEmpty()) { - qscCert = QSslCertificate(crt); - if (qscCert.isNull()) { - log("Failed to parse certificate."); - } else if (qscCert.issuerInfo(QSslCertificate::CommonName) == QLatin1String("Murmur Autogenerated Certificate")) { - log("Old autogenerated certificate is unusable for registration, invalidating it"); - qscCert = QSslCertificate(); - } - } + QList ql; - if (! key.isEmpty() && qscCert.isNull()) { - qscCert = QSslCertificate(key); - if (! qscCert.isNull()) { - log("Using certificate from key."); - } + if (! key.isEmpty()) { + qskKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, pass); + if (qskKey.isNull()) + qskKey = QSslKey(key, QSsl::Dsa, QSsl::Pem, QSsl::PrivateKey, pass); } - - if (! qscCert.isNull()) { - QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm(); - - if (! key.isEmpty()) { - qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, pass); - if (qskKey.isNull()) { - log("Failed to parse key."); + if (qskKey.isNull() && ! crt.isEmpty()) { + qskKey = QSslKey(crt, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, pass); + if (qskKey.isNull()) + qskKey = QSslKey(crt, QSsl::Dsa, QSsl::Pem, QSsl::PrivateKey, pass); + } + if (! qskKey.isNull()) { + ql << QSslCertificate::fromData(crt); + ql << QSslCertificate::fromData(key); + for (int i=0;i(const_cast("Murmur Autogenerated Certificate v2")), -1, -1, 0); X509_set_issuer_name(x509, name); - add_ext(x509, NID_basic_constraints, "critical,CA:FALSE"); - add_ext(x509, NID_ext_key_usage, "serverAuth,clientAuth"); - add_ext(x509, NID_subject_key_identifier, "hash"); - add_ext(x509, NID_netscape_comment, "Generated from murmur"); + add_ext(x509, NID_basic_constraints, SSL_STRING("critical,CA:FALSE")); + add_ext(x509, NID_ext_key_usage, SSL_STRING("serverAuth,clientAuth")); + add_ext(x509, NID_subject_key_identifier, SSL_STRING("hash")); + add_ext(x509, NID_netscape_comment, SSL_STRING("Generated from murmur")); - X509_sign(x509, pkey, EVP_md5()); + X509_sign(x509, pkey, EVP_sha1()); crt.resize(i2d_X509(x509, NULL)); unsigned char *dptr=reinterpret_cast(crt.data()); @@ -149,16 +181,6 @@ void Server::initializeCert() { setConf("key", qskKey.toPem()); } } - - QList pref; - foreach(QSslCipher c, QSslSocket::defaultCiphers()) { - if (c.usedBits() < 128) - continue; - pref << c; - } - if (pref.isEmpty()) - qFatal("No ciphers of at least 128 bit found"); - QSslSocket::setDefaultCiphers(pref); } const QString Server::getDigest() const { diff --git a/src/murmur/Meta.cpp b/src/murmur/Meta.cpp index 8d1da65..bf557bc 100644 --- a/src/murmur/Meta.cpp +++ b/src/murmur/Meta.cpp @@ -199,9 +199,26 @@ void MetaParams::read(QString fname) { QString qsSSLCert = qs.value("sslCert").toString(); QString qsSSLKey = qs.value("sslKey").toString(); + QString qsSSLCA = qs.value("sslCA").toString(); qbaPassPhrase = qs.value("sslPassPhrase").toByteArray(); + if (! qsSSLCA.isEmpty()) { + QFile pem(qsSSLCA); + if (pem.open(QIODevice::ReadOnly)) { + QByteArray qba = pem.readAll(); + pem.close(); + QList ql = QSslCertificate::fromData(qba); + if (ql.isEmpty()) { + qCritical("Failed to parse any CA certificates from %s", qPrintable(qsSSLCA)); + } else { + QSslSocket::addDefaultCaCertificates(ql); + } + } else { + qCritical("Failed to read %s", qPrintable(qsSSLCert)); + } + } + QByteArray crt, key; if (! qsSSLCert.isEmpty()) { @@ -223,45 +240,54 @@ void MetaParams::read(QString fname) { } } - if (! crt.isEmpty()) { - qscCert = QSslCertificate(crt); - if (qscCert.isNull()) { - qCritical("Failed to parse certificate."); + if (! key.isEmpty() || ! crt.isEmpty()) { + if (! key.isEmpty()) { + qskKey = QSslKey(key, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, qbaPassPhrase); + if (qskKey.isNull()) + qskKey = QSslKey(key, QSsl::Dsa, QSsl::Pem, QSsl::PrivateKey, qbaPassPhrase); } - } - - if (! key.isEmpty() && qscCert.isNull()) { - qscCert = QSslCertificate(key); - if (! qscCert.isNull()) { - qWarning("Using certificate from key file."); + if (qskKey.isNull() && ! crt.isEmpty()) { + qskKey = QSslKey(crt, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, qbaPassPhrase); + if (qskKey.isNull()) + qskKey = QSslKey(crt, QSsl::Dsa, QSsl::Pem, QSsl::PrivateKey, qbaPassPhrase); + if (! qskKey.isNull()) + qCritical("Using private key found in certificate file."); } - } - - if (! qscCert.isNull()) { - QSsl::KeyAlgorithm alg = qscCert.publicKey().algorithm(); - - if (! key.isEmpty()) { - qskKey = QSslKey(key, alg, QSsl::Pem, QSsl::PrivateKey, qbaPassPhrase); - if (qskKey.isNull()) { - qCritical("Failed to parse key file."); + if (qskKey.isNull()) + qFatal("No private key found in certificate or key file."); + + QList ql = QSslCertificate::fromData(crt); + ql << QSslCertificate::fromData(key); + for (int i=0;i 0) { + QSslSocket::addDefaultCaCertificates(ql); + qCritical("Adding %d CA certificates from certificate file.", ql.size()); } - if (qskKey.isNull()) - qscCert = QSslCertificate(); } - if (qscCert.isNull() || qskKey.isNull()) { - if (! key.isEmpty() || ! crt.isEmpty()) { - qFatal("Certificate specified, but failed to load."); - } + if (! QSslSocket::supportsSsl()) { + qFatal("Qt without SSL Support"); + } + + QList pref; + foreach(QSslCipher c, QSslSocket::defaultCiphers()) { + if (c.usedBits() < 128) + continue; + pref << c; } + if (pref.isEmpty()) + qFatal("No SSL ciphers of at least 128 bit found"); + QSslSocket::setDefaultCiphers(pref); qmConfig.clear(); qmConfig.insert(QLatin1String("host"),qhaBind.toString()); diff --git a/src/murmur/Server.cpp b/src/murmur/Server.cpp index e5c5b5c..3c8b663 100644 --- a/src/murmur/Server.cpp +++ b/src/murmur/Server.cpp @@ -596,6 +596,7 @@ void Server::newClient() { } } + sock->addCaCertificates(qlCA); sock->setPrivateKey(qskKey); sock->setLocalCertificate(qscCert); diff --git a/src/murmur/Server.h b/src/murmur/Server.h index a4f4def..d9bbf18 100644 --- a/src/murmur/Server.h +++ b/src/murmur/Server.h @@ -129,6 +129,7 @@ class Server : public QThread, public MessageHandler { QRegExp qrPlayerName; QRegExp qrChannelName; + QList qlCA; QSslCertificate qscCert; QSslKey qskKey; QByteArray qbaPassPhrase; @@ -150,6 +151,7 @@ class Server : public QThread, public MessageHandler { // Certificate stuff, implemented partially in Cert.cpp public: + static bool isKeyForCert(const QSslKey &key, const QSslCertificate &cert); void initializeCert(); const QString getDigest() const; -- 1.6.3.3