aboutsummaryrefslogtreecommitdiffstats
path: root/modules/haproxy.cpp
diff options
context:
space:
mode:
authorGravatar Sadie Powell2026-06-06 13:47:59 +0100
committerGravatar Sadie Powell2026-06-06 13:47:59 +0100
commit65603ba4aa8c8ad3b6c4dac6018962934246e7fa (patch)
tree0f41b5da15ff404e58c32f86f6ada9fd3906ac2f /modules/haproxy.cpp
parentUpdate my email address. (diff)
parentFix 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.cpp77
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;
}