diff options
| author | 2026-06-06 13:47:59 +0100 | |
|---|---|---|
| committer | 2026-06-06 13:47:59 +0100 | |
| commit | 65603ba4aa8c8ad3b6c4dac6018962934246e7fa (patch) | |
| tree | 0f41b5da15ff404e58c32f86f6ada9fd3906ac2f /modules/haproxy.cpp | |
| parent | Update my email address. (diff) | |
| parent | Fix escaping LDAP search filters in ldapauth and ldapoper. (diff) | |
Merge branch 'insp4' into master.
Diffstat (limited to 'modules/haproxy.cpp')
| -rw-r--r-- | modules/haproxy.cpp | 77 |
1 files changed, 70 insertions, 7 deletions
diff --git a/modules/haproxy.cpp b/modules/haproxy.cpp index f1259bc78..bd0682ed1 100644 --- a/modules/haproxy.cpp +++ b/modules/haproxy.cpp @@ -57,6 +57,9 @@ enum // The identifier for a TLS TLV entry. PP2_TYPE_SSL = 0x20, + // The identifier for a TLS certificate fingerprint TLV entry. + PP2_TYPE_CERTFP = 0xE0, + // The minimum length of a PP2_TYPE_SSL TLV entry. PP2_TYPE_SSL_LENGTH = 5, @@ -111,7 +114,15 @@ struct HAProxyCertificate final { HAProxyCertificate() { - this->error ="HAProxy does not forward client TLS certificates"; + this->error = "HAProxy does not forward client TLS certificates"; + } + + inline void SetFingerprint(std::string&& fingerprint) + { + this->fingerprints.push_back(std::move(fingerprint)); + this->error.clear(); + this->error.shrink_to_fit(); + this->valid = true; } }; @@ -158,6 +169,12 @@ private: // The API for interacting with user TLS internals. TLS::API& tlsapi; + // The fingerprint forwarded by the proxy for the client certificate. + std::string certificate_fingerprint; + + // The fake certificate we attach for this connection, if any. + std::shared_ptr<HAProxyCertificate> certificate; + // The current state of the PROXY parser. HAProxyState state = HPS_WAITING_FOR_HEADER; @@ -180,17 +197,57 @@ private: } // What type of TLV are we parsing? - switch (recvq[start_index]) + switch (static_cast<uint8_t>(recvq[start_index])) { case PP2_TYPE_SSL: if (!ReadProxyTLVSSL(sock, start_index + PP2_TLV_LENGTH, length)) return 0; break; + + case PP2_TYPE_CERTFP: + if (!ReadProxyTLVCertFP(sock, start_index + PP2_TLV_LENGTH, length)) + return 0; + break; } return PP2_TLV_LENGTH + length; } + bool ReadProxyTLVCertFP(StreamSocket* sock, size_t start_index, uint16_t buffer_length) + { + // If the socket is not a user socket we don't have to do + // anything with this TLV's information. + if (sock->type != StreamSocket::SS_USER) + return true; + + // The fingerprint must be a non-empty hex-encoded digest of a plausible size. + // A hex string must have an even length and at most 128 chars (64-byte digest). + if (buffer_length == 0 || buffer_length % 2 != 0 || buffer_length > 128) + { + ServerInstance->Logs.Debug(MODNAME, "Ignoring PP2_TYPE_CERTFP TLV with unexpected length {}", buffer_length); + return true; + } + + std::string& recvq = GetRecvQ(); + certificate_fingerprint.assign(&recvq[start_index], buffer_length); + + // Verify every character is a valid hexadecimal digit. + if (!std::all_of(certificate_fingerprint.begin(), certificate_fingerprint.end(), [](unsigned char c) { return std::isxdigit(c); })) + { + ServerInstance->Logs.Debug(MODNAME, "Ignoring PP2_TYPE_CERTFP TLV with non-hex content"); + return true; + } + + ServerInstance->Logs.Debug(MODNAME, "Received certificate fingerprint from HAProxy: {}", certificate_fingerprint); + + // If the SSL TLV was already processed, patch the existing certificate + // with the fingerprint now that we have it. + if (certificate) + certificate->SetFingerprint(std::move(this->certificate_fingerprint)); + + return true; + } + bool ReadProxyTLVSSL(StreamSocket* sock, size_t start_index, uint16_t buffer_length) { // A TLS TLV must at least consist of client info (uint8_t) and verification info (uint32_t). @@ -215,11 +272,17 @@ private: if ((recvq[start_index] & PP2_CLIENT_SSL) == 0) return true; - // Create a fake TLS cert for the user. Ideally we should use the user's - // TLS client certificate here but as of 2018-10-16 this is not forwarded - // by HAProxy. - auto* luser = sock->GetData<LocalUserIO>()->user; - tlsapi->SetCertificate(luser, std::make_shared<HAProxyCertificate>()); + // Create a fake TLS cert for the user. If the proxy already forwarded a + // certificate fingerprint via PP2_TYPE_CERTFP we use it; otherwise we + // only know the client was connected over TLS. + if (!certificate) + { + this->certificate = std::make_shared<HAProxyCertificate>(); + this->certificate->SetFingerprint(std::move(this->certificate_fingerprint)); + + auto* luser = sock->GetData<LocalUserIO>()->user; + tlsapi->SetCertificate(luser, this->certificate);; + } return true; } |
