drogonframework / drogonframework/drogon

mTLS support broken - crashes with VerifyMode or doesn't enforce without caPath

Open
#2,433 10 comments 0 reactions 1 assignee Claimed by @marty1885 View on GitHub
Dominant language
C++
Stars
14.3k
Forks
1.4k
Avg merge
1d 13h
Merged PRs (30d)
14

Description

Hi, me and my team have been developing several servers with both WebSockets and HTTP controllers using the Drogon framework. It has proven very useful and helpful, significantly simplifying our work, so let me first congratulate Drogon team for this great tool!

However, we believe to have found several issues that cannot be easily bypassed without directly modifying the Drogon or Trantor source code.

## Problem

Mutual TLS (mTLS) is broken in Drogon. There are two related issues apparently:

1. **Using `VerifyMode: Require` in `sslConfCmds` causes server crash after TLS handshake**
2. **Using `ClientCAFile` without `VerifyMode` doesn't enforce client certificates**

## Issue 1: Server crashes with VerifyMode

**Configuration:**
```cpp
std::vector> ssl_config = {
{"ClientCAFile", "/path/to/ca.pem"},
{"VerifyMode", "Require"}
};
app.addListener("0.0.0.0", 8444, true, cert, key, false, ssl_config);
```

**Result:** Server crashes silently after TLS handshake completes.

**Test with openssl s_client:**
```bash
openssl s_client -connect localhost:8444 -cert client.pem -key client-key.pem -CAfile ca.pem
```

**Output:**
```
SSL handshake has read 1278 bytes and written 1826 bytes
Verification: OK
Verify return code: 0 (ok)
---
error:0A000126:SSL routines:ssl3_read_n:unexpected eof while reading
```

**Observations:**
- TLS handshake completes successfully
- Client certificate is verified (Verification: OK)
- Server sends "Acceptable client certificate CA names"
- Connection closes immediately after handshake

## Issue 2: ClientCAFile alone doesn't enforce mTLS

**Configuration:**
```cpp
std::vector> ssl_config = {
{"ClientCAFile", "/path/to/ca.pem"}
// No VerifyMode
};
app.addListener("0.0.0.0", 8444, true, cert, key, false, ssl_config);
```

**Result:** Server works but **never requests client certificates**. I guess this is expected.

**Test:**
```bash
# Without client cert - should fail but doesn't
curl -k https://localhost:8444/endpoint
# Works! No client cert requested
```

## What may work

Using trantor's TLSPolicy directly (example from `trantor/tests/MTLSServer.cc`):
```cpp
auto policy = TLSPolicy::defaultServerPolicy(cert, key);
policy->setCaPath(ca_path).setValidate(true);
server.enableSSL(policy);
```

If this example is correct, this might request and validate client certificates without crashes.

## Root Cause

Looking at `ListenerManager.cc` lines 145-155:
```cpp
auto policy = trantor::TLSPolicy::defaultServerPolicy(cert, key);
policy->setConfCmds(cmds).setUseOldTLS(listener.useOldTLS_);
serverPtr->enableSSL(std::move(policy));
```

**Problem:** `policy->setCaPath()` is never called.

In `trantor/net/inner/tlsprovider/OpenSSLProvider.cc` lines 730-757, the mTLS setup code only executes when:
```cpp
if (!policy.getCaPath().empty())
```

**When using SSL_CONF_cmd:**
- `ClientCAFile` loads CA for OpenSSL but doesn't set `policy.getCaPath()`
- `VerifyMode: Require` enables enforcement in OpenSSL
- But `policy.getCaPath()` remains empty
- Trantor's mTLS initialization code never runs
- Result: incomplete state that causes crash

**When using only `ClientCAFile`:**
- `policy.getCaPath()` is empty
- Trantor's mTLS code doesn't run
- No verification mode is set
- Result: server doesn't request client certificates

## Expected Behavior

Either:
1. SSL_CONF_cmd should work properly without crashes
2. Or `addListener()` should expose `caPath` parameter like trantor's native API

## Workaround

None that works properly. Invalid VerifyMode values are silently ignored, making server work but without any mTLS enforcement.

## Environment

- Drogon: 1.9.11
- OS: Ubuntu 24.04
- Compiler: GCC 11.4
- OpenSSL: 3.0.2

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.