]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Add %ssl::<cert macro for logging server X.509 certificate (#316)
authorDaris A Nevil <daris@nevil.org>
Mon, 17 Dec 2018 17:38:01 +0000 (17:38 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Wed, 19 Dec 2018 21:50:18 +0000 (21:50 +0000)
We have chosen the PEM format instead of, for example, raw DER format
because most programs exchange certificates using PEM format and because
logging raw binary values would be unusual for Squid logformat.

The current support is limited to SslBump step3 which parses and stores
the peer certificate. TODO: Support all from-Squid TLS connections.

src/cf.data.pre
src/format/ByteCode.h
src/format/Format.cc
src/format/Token.cc
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsslsquid.cc
test-suite/squidconf/external_acl_type

index 3df11e2095b2ffc41659d45545386e5c0a9dc7f2..065f1940b9413cd54897e9212eadfbb989cfaac5 100644 (file)
@@ -4769,6 +4769,34 @@ DOC_START
                                not available. Consider encoding the logged
                                value because Issuer often has spaces.
 
+               ssl::<cert
+                               The received server x509 certificate in PEM
+                               format, including BEGIN and END lines (or a
+                               dash ('-') if the certificate is unavailable).
+
+                               WARNING: Large certificates will exceed the
+                               current 8KB access.log record limit, resulting
+                               in truncated records. Such truncation usually
+                               happens in the middle of a record field. The
+                               limit applies to all access logging modules.
+
+                               The logged certificate may have failed
+                               validation and may not be trusted by Squid.
+                               This field does not include any intermediate
+                               certificates that may have been received from
+                               the server or fetched during certificate
+                               validation process.
+
+                               Currently, Squid only collects server
+                               certificates during step3 of SslBump
+                               processing; connections that were not subject
+                               to ssl_bump rules or that did not match a peek
+                               or stare rule at step2 will not have the
+                               server certificate information.
+
+                               This field is using pass-through URL encoding
+                               by default.
+
                ssl::<cert_errors
                                The list of certificate validation errors
                                detected by Squid (including OpenSSL and
index a6f8fd982c70db080ff145e0678eba62db273dac..48f0bcb7f961fd74e1a5fde19589be9986f9738f 100644 (file)
@@ -220,6 +220,7 @@ typedef enum {
     LFT_SSL_SERVER_CERT_SUBJECT,
     LFT_SSL_SERVER_CERT_ISSUER,
     LFT_SSL_SERVER_CERT_ERRORS,
+    LFT_SSL_SERVER_CERT_WHOLE,
     LFT_TLS_CLIENT_NEGOTIATED_VERSION,
     LFT_TLS_SERVER_NEGOTIATED_VERSION,
     LFT_TLS_CLIENT_NEGOTIATED_CIPHER,
index 663cba123ab4e01dd28d61ef368fa1af5631f849..a828ac10db54cf8feac13ac0e75f7ca029a94d21 100644 (file)
@@ -1178,8 +1178,10 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (al->request) {
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
-                    if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetUserCertificatePEM(ssl);
+                    if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+                        sb = sslGetUserCertificatePEM(ssl);
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
@@ -1188,8 +1190,10 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
             if (al->request) {
                 ConnStateData *conn = al->request->clientConnectionManager.get();
                 if (conn && Comm::IsConnOpen(conn->clientConnection)) {
-                    if (auto ssl = fd_table[conn->clientConnection->fd].ssl.get())
-                        out = sslGetUserCertificatePEM(ssl);
+                    if (const auto ssl = fd_table[conn->clientConnection->fd].ssl.get()) {
+                        sb = sslGetUserCertificatePEM(ssl);
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
@@ -1265,13 +1269,20 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_SSL_SERVER_CERT_ISSUER:
         case LFT_SSL_SERVER_CERT_SUBJECT:
+        case LFT_SSL_SERVER_CERT_WHOLE:
             if (al->request && al->request->clientConnectionManager.valid()) {
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
                     if (X509 *serverCert = srvBump->serverCert.get()) {
                         if (fmt->type == LFT_SSL_SERVER_CERT_SUBJECT)
                             out = Ssl::GetX509UserAttribute(serverCert, "DN");
-                        else
+                        else if (fmt->type == LFT_SSL_SERVER_CERT_ISSUER)
                             out = Ssl::GetX509CAAttribute(serverCert, "DN");
+                        else {
+                            assert(fmt->type == LFT_SSL_SERVER_CERT_WHOLE);
+                            sb = Ssl::GetX509PEM(serverCert);
+                            out = sb.c_str();
+                            quote = 1;
+                        }
                     }
                 }
             }
index 4802a5be093c79977243c4164b0671ad907f9d59..daf4bf9d3c78dc31d40dcc46d793c6051df49ab2 100644 (file)
@@ -221,6 +221,7 @@ static TokenTableEntry TokenTableSsl[] = {
     TokenTableEntry("<cert_subject", LFT_SSL_SERVER_CERT_SUBJECT),
     TokenTableEntry("<cert_issuer", LFT_SSL_SERVER_CERT_ISSUER),
     TokenTableEntry("<cert_errors", LFT_SSL_SERVER_CERT_ERRORS),
+    TokenTableEntry("<cert", LFT_SSL_SERVER_CERT_WHOLE),
     TokenTableEntry(">negotiated_version", LFT_TLS_CLIENT_NEGOTIATED_VERSION),
     TokenTableEntry("<negotiated_version", LFT_TLS_SERVER_NEGOTIATED_VERSION),
     TokenTableEntry(">negotiated_cipher", LFT_TLS_CLIENT_NEGOTIATED_CIPHER),
index f6d4ce7aafaab92b779338cabaf861b0acae0503..8f3c54ab2746716a4467e6f4b84113879bedf5e0 100644 (file)
@@ -651,6 +651,19 @@ Ssl::GetX509Fingerprint(X509 * cert, const char *)
     return buf;
 }
 
+SBuf
+Ssl::GetX509PEM(X509 * cert)
+{
+    assert(cert);
+
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
+    PEM_write_bio_X509(bio.get(), cert);
+
+    char *ptr;
+    const auto len = BIO_get_mem_data(bio.get(), &ptr);
+    return SBuf(ptr, len);
+}
+
 /// \ingroup ServerProtocolSSLInternal
 const char *
 Ssl::GetX509CAAttribute(X509 * cert, const char *attribute_name)
@@ -701,80 +714,37 @@ sslGetUserEmail(SSL * ssl)
     return sslGetUserAttribute(ssl, "emailAddress");
 }
 
-const char *
+SBuf
 sslGetUserCertificatePEM(SSL *ssl)
 {
-    X509 *cert;
-    BIO *mem;
-    static char *str = NULL;
-    char *ptr;
-    long len;
-
-    safe_free(str);
-
-    if (!ssl)
-        return NULL;
-
-    cert = SSL_get_peer_certificate(ssl);
-
-    if (!cert)
-        return NULL;
-
-    mem = BIO_new(BIO_s_mem());
+    assert(ssl);
 
-    PEM_write_bio_X509(mem, cert);
+    if (const auto cert = SSL_get_peer_certificate(ssl))
+        return Ssl::GetX509PEM(cert);
 
-    len = BIO_get_mem_data(mem, &ptr);
-
-    str = (char *)xmalloc(len + 1);
-
-    memcpy(str, ptr, len);
-
-    str[len] = '\0';
-
-    X509_free(cert);
-
-    BIO_free(mem);
-
-    return str;
+    return SBuf();
 }
 
-const char *
+SBuf
 sslGetUserCertificateChainPEM(SSL *ssl)
 {
-    STACK_OF(X509) *chain;
-    BIO *mem;
-    static char *str = NULL;
-    char *ptr;
-    long len;
-    int i;
+    assert(ssl);
 
-    safe_free(str);
-
-    if (!ssl)
-        return NULL;
-
-    chain = SSL_get_peer_cert_chain(ssl);
+    auto chain = SSL_get_peer_cert_chain(ssl);
 
     if (!chain)
         return sslGetUserCertificatePEM(ssl);
 
-    mem = BIO_new(BIO_s_mem());
+    Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
 
-    for (i = 0; i < sk_X509_num(chain); ++i) {
+    for (int i = 0; i < sk_X509_num(chain); ++i) {
         X509 *cert = sk_X509_value(chain, i);
-        PEM_write_bio_X509(mem, cert);
+        PEM_write_bio_X509(bio.get(), cert);
     }
 
-    len = BIO_get_mem_data(mem, &ptr);
-
-    str = (char *)xmalloc(len + 1);
-    memcpy(str, ptr, len);
-    str[len] = '\0';
-
-    BIO_free(mem);
-
-    return str;
+    char *ptr;
+    const auto len = BIO_get_mem_data(bio.get(), &ptr);
+    return SBuf(ptr, len);
 }
 
 /// Create SSL context and apply ssl certificate and private key to it.
index 40dc2903aff478bf91019bb98e922c4f4c77ebb6..118d309fdffd8f641c781ace842ef3bdbd42e4ac 100644 (file)
@@ -103,15 +103,16 @@ const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name);
 const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name);
 
 /// \ingroup ServerProtocolSSLAPI
-const char *sslGetUserCertificatePEM(SSL *ssl);
+SBuf sslGetUserCertificatePEM(SSL *ssl);
 
 /// \ingroup ServerProtocolSSLAPI
-const char *sslGetUserCertificateChainPEM(SSL *ssl);
+SBuf sslGetUserCertificateChainPEM(SSL *ssl);
 
 namespace Ssl
 {
 /// \ingroup ServerProtocolSSLAPI
 typedef char const *GETX509ATTRIBUTE(X509 *, const char *);
+typedef SBuf GETX509PEM(X509 *);
 
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509UserAttribute;
@@ -119,6 +120,9 @@ GETX509ATTRIBUTE GetX509UserAttribute;
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509CAAttribute;
 
+/// \ingroup ServerProtocolSSLAPI
+GETX509PEM GetX509PEM;
+
 /// \ingroup ServerProtocolSSLAPI
 GETX509ATTRIBUTE GetX509Fingerprint;
 
index fb47a924c744e466919a2544bb34773239e9e635..35ed7c864cae191d67b10e006c8f239691a519aa 100644 (file)
@@ -11,6 +11,7 @@
 #if USE_OPENSSL
 
 #include "fatal.h"
+#include "sbuf/SBuf.h"
 
 /* Stub File for the ssl/libsslsquid.la convenience library */
 
@@ -59,8 +60,8 @@ void MaybeSetupRsaCallback(Security::ContextPointer &) STUB
 const char *sslGetUserEmail(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
 const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
-const char *sslGetUserCertificatePEM(SSL *ssl) STUB_RETVAL(NULL)
-const char *sslGetUserCertificateChainPEM(SSL *ssl) STUB_RETVAL(NULL)
+SBuf sslGetUserCertificatePEM(SSL *ssl) STUB_RETVAL(SBuf())
+SBuf sslGetUserCertificateChainPEM(SSL *ssl) STUB_RETVAL(SBuf())
 namespace Ssl
 {
 //GETX509ATTRIBUTE GetX509UserAttribute;
@@ -75,6 +76,7 @@ int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(vo
 bool checkX509ServerValidity(X509 *cert, const char *server) STUB_RETVAL(false)
 int asn1timeToString(ASN1_TIME *tm, char *buf, int len) STUB_RETVAL(0)
 void setClientSNI(SSL *ssl, const char *fqdn) STUB
+SBuf GetX509PEM(SSL *ssl) STUB_RETVAL(SBuf())
 } //namespace Ssl
 
 #endif
index 1fef7ff686e37a42ec3bf0d69a4c421c43213167..5274108b977c59ec6e18e8173718f64e9048e0af 100644 (file)
@@ -44,3 +44,4 @@ external_acl_type foo \
 #      %ssl::>sni
 #      %ssl::<cert_subject
 #      %ssl::<cert_issuer
+#      %ssl::<cert