]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/ssl/cert_validate_message.cc
Source Format Enforcement (#763)
[thirdparty/squid.git] / src / ssl / cert_validate_message.cc
index ceee3014d25b435569af02a4946521b96518ac3a..48fab388b7e8dd07534c5e660f300a51cf9ce8be 100644 (file)
@@ -1,43 +1,82 @@
+/*
+ * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
 #include "squid.h"
 #include "acl/FilledChecklist.h"
-#include "ssl/support.h"
+#include "globals.h"
+#include "helper.h"
+#include "security/CertError.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/ErrorDetail.h"
+#include "ssl/support.h"
+#include "util.h"
+
+/// Retrieves the certificates chain used to verify the peer.
+/// This is the full chain built by OpenSSL while verifying the server
+/// certificate or, if this is not available, the chain sent by server.
+/// \return the certificates chain or nil
+static STACK_OF(X509) *
+PeerValidationCertificatesChain(const Security::SessionPointer &ssl)
+{
+    assert(ssl);
+    // The full chain built by openSSL while verifying the server cert,
+    // retrieved from verify callback:
+    if (const auto certs = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_ssl_cert_chain)))
+        return certs;
+
+    /// Last resort: certificates chain sent by server
+    return SSL_get_peer_cert_chain(ssl.get()); // may be nil
+}
 
 void
 Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
 {
     body.clear();
     body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
-    if (vcert.errors) {
-        body += "\n" + Ssl::CertValidationMsg::param_error + "=";
-        bool comma = false;
-        for (const Ssl::Errors *err = vcert.errors; err; err = err->next ) {
-            if (comma)
-                body += ",";
-            body += GetErrorName(err->element);
-            comma = true;
-        }
-    }
 
-    if (vcert.peerCerts) {
-        body +="\n";
+    if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
+        body += "\n" +  Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
+
+    if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
+        body += "\n" +  Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
+
+    STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(vcert.ssl);
+    if (peerCerts) {
         Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
-        for (int i = 0; i < sk_X509_num(vcert.peerCerts); ++i) {
-            X509 *cert = sk_X509_value(vcert.peerCerts, i);
+        for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
+            X509 *cert = sk_X509_value(peerCerts, i);
             PEM_write_bio_X509(bio.get(), cert);
-            body = body + "cert_" + xitoa(i) + "=";
+            body = body + "\n" + param_cert + xitoa(i) + "=";
             char *ptr;
             long len = BIO_get_mem_data(bio.get(), &ptr);
-            body.append(ptr, len);
-            // Normally openssl toolkit terminates Certificate with a '\n'.
-            if (ptr[len-1] != '\n')
-                body +="\n";
+            body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
             if (!BIO_reset(bio.get())) {
                 // print an error?
             }
         }
     }
+
+    if (vcert.errors) {
+        int i = 0;
+        for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
+            body +="\n";
+            body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
+            int errorCertPos = -1;
+            if (err->element.cert.get())
+                errorCertPos = sk_X509_find(peerCerts, err->element.cert.get());
+            if (errorCertPos < 0) {
+                // assert this error ?
+                debugs(83, 4, "WARNING: wrong cert in cert validator request");
+            }
+            body += param_error_cert + xitoa(i) + "=";
+            body += param_cert + xitoa((errorCertPos >= 0 ? errorCertPos : 0));
+        }
+    }
 }
 
 static int
@@ -46,14 +85,16 @@ get_error_id(const char *label, size_t len)
     const char *e = label + len -1;
     while (e != label && xisdigit(*e)) --e;
     if (e != label) ++e;
-    return strtol(e, 0 , 10);
+    return strtol(e, 0, 10);
 }
 
 bool
-Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp, STACK_OF(X509) *peerCerts, std::string &error)
+Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp, std::string &error)
 {
     std::vector<CertItem> certs;
 
+    const STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(resp.ssl);
+
     const char *param = body.c_str();
     while (*param) {
         while (xisspace(*param)) param++;
@@ -71,7 +112,7 @@ Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp, STACK_OF(X50
                 strncmp(param, param_cert.c_str(), param_cert.length()) == 0) {
             CertItem ci;
             ci.name.assign(param, param_len);
-            X509_Pointer x509;
+            Security::CertPointer x509;
             readCertFromMemory(x509, value);
             ci.setCert(x509.get());
             certs.push_back(ci);
@@ -121,18 +162,22 @@ Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp, STACK_OF(X50
                 //if certId is not correct sk_X509_value returns NULL
                 currentItem.setCert(sk_X509_value(peerCerts, certId));
             }
+        } else if (param_len > param_error_depth.length() &&
+                   strncmp(param, param_error_depth.c_str(), param_error_depth.length()) == 0 &&
+                   std::all_of(v.begin(), v.end(), isdigit)) {
+            currentItem.error_depth = atoi(v.c_str());
         } else {
             debugs(83, DBG_IMPORTANT, "WARNING: cert validator response parse error: Unknown parameter name " << std::string(param, param_len).c_str());
             return false;
         }
 
-        param = value + value_len +1;
+        param = value + value_len;
     }
 
     /*Run through parsed errors to check for errors*/
     typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
     for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
-        if (i->error_no != SSL_ERROR_NONE) {
+        if (i->error_no == SSL_ERROR_NONE) {
             debugs(83, DBG_IMPORTANT, "WARNING: cert validator incomplete response: Missing error name from error_id: " << i->id);
             return false;
         }
@@ -152,6 +197,13 @@ Ssl::CertValidationMsg::getCertByName(std::vector<CertItem> const &certs, std::s
     return NULL;
 }
 
+uint64_t
+Ssl::CertValidationResponse::MemoryUsedByResponse(const CertValidationResponse::Pointer &)
+{
+    // XXX: This math does not account for most of the response size!
+    return sizeof(CertValidationResponse);
+}
+
 Ssl::CertValidationResponse::RecvdError &
 Ssl::CertValidationResponse::getError(int errorId)
 {
@@ -166,42 +218,12 @@ Ssl::CertValidationResponse::getError(int errorId)
     return errors.back();
 }
 
-Ssl::CertValidationResponse::RecvdError::RecvdError(const RecvdError &old)
-{
-    id = old.id;
-    error_no = old.error_no;
-    error_reason = old.error_reason;
-    setCert(old.cert.get());
-}
-
-Ssl::CertValidationResponse::RecvdError & Ssl::CertValidationResponse::RecvdError::operator = (const RecvdError &old)
-{
-    id = old.id;
-    error_no = old.error_no;
-    error_reason = old.error_reason;
-    setCert(old.cert.get());
-    return *this;
-}
-
 void
 Ssl::CertValidationResponse::RecvdError::setCert(X509 *aCert)
 {
     cert.resetAndLock(aCert);
 }
 
-Ssl::CertValidationMsg::CertItem::CertItem(const CertItem &old)
-{
-    name = old.name;
-    setCert(old.cert.get());
-}
-
-Ssl::CertValidationMsg::CertItem & Ssl::CertValidationMsg::CertItem::operator = (const CertItem &old)
-{
-    name = old.name;
-    setCert(old.cert.get());
-    return *this;
-}
-
 void
 Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
 {
@@ -210,9 +232,11 @@ Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
 
 const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
 const std::string Ssl::CertValidationMsg::param_domain("domain");
-const std::string Ssl::CertValidationMsg::param_error("errors");
 const std::string Ssl::CertValidationMsg::param_cert("cert_");
 const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
 const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
 const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
+const std::string Ssl::CertValidationMsg::param_error_depth("error_depth_");
+const std::string Ssl::CertValidationMsg::param_proto_version("proto_version");
+const std::string Ssl::CertValidationMsg::param_cipher("cipher");