]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/cert_validate_message.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / ssl / cert_validate_message.cc
CommitLineData
bbc27441 1/*
b8ae064d 2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
bbc27441
AJ
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
a1f04d64 9#include "squid.h"
77dce8a5 10#include "acl/FilledChecklist.h"
4747ea4c 11#include "globals.h"
a9b6afe1 12#include "helper.h"
ac1b16b5 13#include "sbuf/Stream.h"
92e3827b 14#include "security/CertError.h"
a1f04d64
AR
15#include "ssl/cert_validate_message.h"
16#include "ssl/ErrorDetail.h"
602d9612 17#include "ssl/support.h"
ed6e9fb9 18#include "util.h"
a1f04d64 19
8fe1a85a
CT
20/// Retrieves the certificates chain used to verify the peer.
21/// This is the full chain built by OpenSSL while verifying the server
22/// certificate or, if this is not available, the chain sent by server.
23/// \return the certificates chain or nil
24static STACK_OF(X509) *
25PeerValidationCertificatesChain(const Security::SessionPointer &ssl)
26{
27 assert(ssl);
28 // The full chain built by openSSL while verifying the server cert,
29 // retrieved from verify callback:
30 if (const auto certs = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_ssl_cert_chain)))
31 return certs;
32
33 /// Last resort: certificates chain sent by server
34 return SSL_get_peer_cert_chain(ssl.get()); // may be nil
35}
36
3a7d782f 37void
b56756cb 38Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
a1f04d64
AR
39{
40 body.clear();
b56756cb 41 body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
4747ea4c 42
0b168d25 43 if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
6e325882
CT
44 body += "\n" + Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
45
0b168d25 46 if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
6e325882
CT
47 body += "\n" + Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
48
8fe1a85a 49 STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(vcert.ssl);
14798e73 50 if (peerCerts) {
a1f04d64 51 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
14798e73
CT
52 for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
53 X509 *cert = sk_X509_value(peerCerts, i);
a1f04d64 54 PEM_write_bio_X509(bio.get(), cert);
62a7607e 55 body = body + "\n" + param_cert + xitoa(i) + "=";
a1f04d64
AR
56 char *ptr;
57 long len = BIO_get_mem_data(bio.get(), &ptr);
62a7607e 58 body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
a1f04d64
AR
59 if (!BIO_reset(bio.get())) {
60 // print an error?
61 }
62 }
63 }
62a7607e
CT
64
65 if (vcert.errors) {
66 int i = 0;
92e3827b 67 for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
62a7607e
CT
68 body +="\n";
69 body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
70 int errorCertPos = -1;
170cb017 71 if (err->element.cert.get())
62a7607e
CT
72 errorCertPos = sk_X509_find(peerCerts, err->element.cert.get());
73 if (errorCertPos < 0) {
74 // assert this error ?
75 debugs(83, 4, "WARNING: wrong cert in cert validator request");
76 }
77 body += param_error_cert + xitoa(i) + "=";
78 body += param_cert + xitoa((errorCertPos >= 0 ? errorCertPos : 0));
79 }
80 }
a1f04d64
AR
81}
82
3a7d782f
CT
83static int
84get_error_id(const char *label, size_t len)
a1f04d64
AR
85{
86 const char *e = label + len -1;
87 while (e != label && xisdigit(*e)) --e;
88 if (e != label) ++e;
aee3523a 89 return strtol(e, nullptr, 10);
a1f04d64
AR
90}
91
3a7d782f 92bool
8b082ed9 93Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp)
ac1b16b5
AR
94{
95 try {
96 tryParsingResponse(resp);
97 return true;
98 }
99 catch (...) {
100 debugs(83, DBG_IMPORTANT, "ERROR: Cannot parse sslcrtvalidator_program response:" <<
101 Debug::Extra << "problem: " << CurrentException);
102 return false;
103 }
104}
105
106/// implements primary parseResponse() functionality until that method callers
107/// are ready to handle exceptions
108void
109Ssl::CertValidationMsg::tryParsingResponse(CertValidationResponse &resp)
a1f04d64 110{
3a7d782f 111 std::vector<CertItem> certs;
a1f04d64 112
8fe1a85a
CT
113 const STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(resp.ssl);
114
a1f04d64 115 const char *param = body.c_str();
22636a68
CT
116 while (*param) {
117 while (xisspace(*param)) param++;
a1f04d64
AR
118 if (! *param)
119 break;
120
121 size_t param_len = strcspn(param, "=\r\n");
122 if (param[param_len] != '=') {
ac1b16b5 123 throw TextException(ToSBuf("Missing parameter value: ", param), Here());
a1f04d64
AR
124 }
125 const char *value=param+param_len+1;
126
22636a68
CT
127 if (param_len > param_cert.length() &&
128 strncmp(param, param_cert.c_str(), param_cert.length()) == 0) {
3a7d782f 129 CertItem ci;
22636a68 130 ci.name.assign(param, param_len);
ac1b16b5 131 const auto x509 = ReadCertificate(ReadOnlyBioTiedTo(value));
a1f04d64
AR
132 ci.setCert(x509.get());
133 certs.push_back(ci);
134
135 const char *b = strstr(value, "-----END CERTIFICATE-----");
aee3523a 136 if (b == nullptr) {
ac1b16b5 137 throw TextException(ToSBuf("Missing END CERTIFICATE boundary: ", value), Here());
a1f04d64
AR
138 }
139 b += strlen("-----END CERTIFICATE-----");
140 param = b + 1;
141 continue;
142 }
143
144 size_t value_len = strcspn(value, "\r\n");
145 std::string v(value, value_len);
146
4a77bb4e 147 debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " <<
a1f04d64
AR
148 v.c_str());
149
150 int errorId = get_error_id(param, param_len);
b56756cb 151 Ssl::CertValidationResponse::RecvdError &currentItem = resp.getError(errorId);
a1f04d64 152
22636a68
CT
153 if (param_len > param_error_name.length() &&
154 strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0) {
a1f04d64 155 currentItem.error_no = Ssl::GetErrorCode(v.c_str());
3a7d782f 156 if (currentItem.error_no == SSL_ERROR_NONE) {
ac1b16b5 157 throw TextException(ToSBuf("Unknown TLS error name: ", v), Here());
3a7d782f 158 }
22636a68 159 } else if (param_len > param_error_reason.length() &&
a1f04d64
AR
160 strncmp(param, param_error_reason.c_str(), param_error_reason.length()) == 0) {
161 currentItem.error_reason = v;
162 } else if (param_len > param_error_cert.length() &&
163 strncmp(param, param_error_cert.c_str(), param_error_cert.length()) == 0) {
164
3a7d782f 165 if (X509 *cert = getCertByName(certs, v)) {
4a77bb4e 166 debugs(83, 6, "The certificate with id \"" << v << "\" found.");
3a7d782f
CT
167 currentItem.setCert(cert);
168 } else {
169 //In this case we assume that the certID is one of the certificates sent
170 // to cert validator. The certificates sent to cert validator have names in
171 // form "cert_xx" where the "xx" is an integer represents the position of
172 // the certificate inside peer certificates list.
b56756cb 173 const int certId = get_error_id(v.c_str(), v.length());
4a77bb4e 174 debugs(83, 6, "Cert index in peer certificates list:" << certId);
77dce8a5
CT
175 //if certId is not correct sk_X509_value returns NULL
176 currentItem.setCert(sk_X509_value(peerCerts, certId));
a1f04d64 177 }
b4e6a8d4
CT
178 } else if (param_len > param_error_depth.length() &&
179 strncmp(param, param_error_depth.c_str(), param_error_depth.length()) == 0 &&
180 std::all_of(v.begin(), v.end(), isdigit)) {
96e44f2e 181 currentItem.error_depth = atoi(v.c_str());
3a7d782f 182 } else {
ac1b16b5 183 throw TextException(ToSBuf("Unknown parameter name: ", std::string(param, param_len)), Here());
3a7d782f 184 }
a1f04d64 185
77da1d91 186 param = value + value_len;
a1f04d64
AR
187 }
188
3a7d782f 189 /*Run through parsed errors to check for errors*/
bc624572
CT
190 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
191 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
46901eed 192 if (i->error_no == SSL_ERROR_NONE) {
ac1b16b5 193 throw TextException(ToSBuf("Incomplete response; missing error name from error_id: ", i->id), Here());
bc624572
CT
194 }
195 }
a1f04d64
AR
196}
197
3a7d782f 198X509 *
b56756cb 199Ssl::CertValidationMsg::getCertByName(std::vector<CertItem> const &certs, std::string const & name)
3a7d782f 200{
6136ab56
CT
201 typedef std::vector<CertItem>::const_iterator SVCI;
202 for (SVCI ci = certs.begin(); ci != certs.end(); ++ci) {
3a7d782f 203 if (ci->name.compare(name) == 0)
4a77bb4e 204 return ci->cert.get();
3a7d782f 205 }
aee3523a 206 return nullptr;
3a7d782f
CT
207}
208
72247610
AJ
209uint64_t
210Ssl::CertValidationResponse::MemoryUsedByResponse(const CertValidationResponse::Pointer &)
211{
212 // XXX: This math does not account for most of the response size!
213 return sizeof(CertValidationResponse);
214}
215
b56756cb
CT
216Ssl::CertValidationResponse::RecvdError &
217Ssl::CertValidationResponse::getError(int errorId)
3a7d782f 218{
6136ab56 219 typedef Ssl::CertValidationResponse::RecvdErrors::iterator SVCREI;
22636a68 220 for (SVCREI i = errors.begin(); i != errors.end(); ++i) {
3a7d782f
CT
221 if (i->id == errorId)
222 return *i;
223 }
b56756cb 224 Ssl::CertValidationResponse::RecvdError errItem;
3a7d782f
CT
225 errItem.id = errorId;
226 errors.push_back(errItem);
227 return errors.back();
228}
229
a1f04d64 230void
22636a68
CT
231Ssl::CertValidationResponse::RecvdError::setCert(X509 *aCert)
232{
4a77bb4e 233 cert.resetAndLock(aCert);
a1f04d64
AR
234}
235
a1f04d64 236void
b56756cb 237Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
a1f04d64 238{
4a77bb4e 239 cert.resetAndLock(aCert);
a1f04d64
AR
240}
241
b56756cb
CT
242const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
243const std::string Ssl::CertValidationMsg::param_domain("domain");
b56756cb 244const std::string Ssl::CertValidationMsg::param_cert("cert_");
22636a68 245const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
b56756cb
CT
246const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
247const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
b4e6a8d4 248const std::string Ssl::CertValidationMsg::param_error_depth("error_depth_");
6e325882
CT
249const std::string Ssl::CertValidationMsg::param_proto_version("proto_version");
250const std::string Ssl::CertValidationMsg::param_cipher("cipher");
f53969cc 251