# Use OpenSSL and Qt to build the correct X509 certificate chain based on a pool and a leaf

This is my solution to a question I asked on stackoverflow.

The scenario is as follows: I got my X509 certificate and a pool of other X509 certificates. Now I want to calculate the chain of trust, starting from my certificate based on the pool.

I realized that I can use openSSL in addition to Qt in the project, after digging in the deeps of their documentation, I came up with the following solution. I am a bit concerned that I get memory leaks somewhere. Also, and this might be the most critical part, I don't know if I used the flag paramter for the SSL_CTX_build_cert_chain call correctly.

Btw I also asked for the theoretical background how such a chain is calculated in general (based on the subject and issuer, what fields have to match, e.g. only common name?), so if you know the answer, head over there and share it please :)!

Finally, here is my code:

QList< QSslCertificate > buildSslChain(const QSslCertificate &leaf, const QList< QSslCertificate > &pool) {
QList< QSslCertificate > chain;
if (leaf.isNull()) {
return chain;
}
chain << leaf;
if (pool.isEmpty()) {
return chain;
}

// Convert the leaf to der format and create an openssl x509 from it
QByteArray qbaLeaf = leaf.toDer();
int maxDerSize     = qbaLeaf.size();
BIO *mem           = BIO_new_mem_buf(qbaLeaf.data(), maxDerSize);
Q_UNUSED(BIO_set_close(mem, BIO_NOCLOSE));
X509 *leaf_x509 = d2i_X509_bio(mem, nullptr);
BIO_free(mem);

// Prepare a ssl context, the method should not matter, so just go with TLS_method()
SSL_CTX *ctx = SSL_CTX_new(TLS_method());

SSL_CTX_use_certificate(ctx, leaf_x509);

// Construct openssl x509 for the pool and add each to the ctx
foreach (const QSslCertificate &cert, pool) {
QByteArray qbaCert = cert.toDer();
int s              = qbaCert.size();
maxDerSize         = maxDerSize < s ? s : maxDerSize;
BIO *mem           = BIO_new_mem_buf(qbaCert.data(), s);
Q_UNUSED(BIO_set_close(mem, BIO_NOCLOSE));
X509 *x509 = d2i_X509_bio(mem, nullptr);
BIO_free(mem);
}

// Do the actual chain building
int flags = SSL_BUILD_CHAIN_FLAG_CHECK | SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR; // Am I using the correct flags?!
int ret   = SSL_CTX_build_cert_chain(ctx, flags);

// Check if we were succesfull, since we use the SSL_BUILD_CHAIN_FLAG_IGNORE_ERROR flag
// 2 is a acceptable return value, too
if (ret == 1 || ret == 2) {
// Retrieve the chain
STACK_OF(X509) *stack = nullptr;
SSL_CTX_get0_chain_certs(ctx, &stack);

// Copy the chain back to qt
// Instead of allocating a new buffer every time i2d_X509 is called, we allocate a shared buffer
// and because we know the maxDerSize, we konw how big this needs to be
unsigned char *buffer = (unsigned char *) malloc(maxDerSize);
while (sk_X509_num(stack) > 0) {
X509 *next     = sk_X509_shift(stack);
int actualSize = i2d_X509(next, &buffer);
X509_free(next);
if (actualSize == -1) {
// Failed to der encode certificate in openssl
chain.clear();
break;
}
// i2d_X509 altered our buffer pointer, so we need to set it back manually
buffer -= actualSize;
QByteArray array            = QByteArray::fromRawData((char *) buffer, actualSize);
QList< QSslCertificate > ql = QSslCertificate::fromData(array, QSsl::EncodingFormat::Der);
if (ql.size() == 1) {
chain << ql;
} else {
// Data from openssl must contain exactly one certificate!
chain.clear();
break;
}
}

// Clean up
free(buffer);
} else {
chain.clear();
}
// so they will automatically be freed if the ctx is freed.
// Same for the stack, which was obtain with the set0 verison.
SSL_CTX_free(ctx);
X509_free(leaf_x509);

// Drain OpenSSL's per-thread error queue
// to ensure that errors from the operations
// we've done in here do not leak out into
// Qt's SSL module.
//
// If an error leaks, it can break all connections
// to the server because each invocation of Qt's SSL
// queue (albeit indirectly, via SSL_get_error()).
// Qt expects any errors returned from SSL_get_error()
// to be related to the QSslSocket it is currently
// processing -- which is the obvious thing to expect:
// SSL_get_error() takes a pointer to an SSL object
// and the return code of the failed operation.
// However, it is also documented as:
//
//  "In addition to ssl and ret, SSL_get_error()
//   inspects the current thread's OpenSSL error
//   queue."
//
// So, if any OpenSSL operation on the main thread
// forgets to clear the error queue, those errors
// *will* leak into other things that *do* error
// checking. In our case, into Qt's SSL read callback,
// resulting in all clients being disconnected.
ERR_clear_error();

return chain;
}


Edit: I realized that it should probably be sk_X509_shift instead of sk_X509_pop so I changed that.