]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/cert_validate_message.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / ssl / cert_validate_message.cc
1 /*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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
9 #include "squid.h"
10 #include "acl/FilledChecklist.h"
11 #include "globals.h"
12 #include "helper.h"
13 #include "sbuf/Stream.h"
14 #include "security/CertError.h"
15 #include "ssl/cert_validate_message.h"
16 #include "ssl/ErrorDetail.h"
17 #include "ssl/support.h"
18 #include "util.h"
19
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
24 static STACK_OF(X509) *
25 PeerValidationCertificatesChain(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
37 void
38 Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
39 {
40 body.clear();
41 body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
42
43 if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
44 body += "\n" + Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
45
46 if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
47 body += "\n" + Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
48
49 STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(vcert.ssl);
50 if (peerCerts) {
51 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
52 for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
53 X509 *cert = sk_X509_value(peerCerts, i);
54 PEM_write_bio_X509(bio.get(), cert);
55 body = body + "\n" + param_cert + xitoa(i) + "=";
56 char *ptr;
57 long len = BIO_get_mem_data(bio.get(), &ptr);
58 body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
59 if (!BIO_reset(bio.get())) {
60 // print an error?
61 }
62 }
63 }
64
65 if (vcert.errors) {
66 int i = 0;
67 for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
68 body +="\n";
69 body = body + param_error_name + xitoa(i) + "=" + GetErrorName(err->element.code) + "\n";
70 int errorCertPos = -1;
71 if (err->element.cert.get())
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 }
81 }
82
83 static int
84 get_error_id(const char *label, size_t len)
85 {
86 const char *e = label + len -1;
87 while (e != label && xisdigit(*e)) --e;
88 if (e != label) ++e;
89 return strtol(e, nullptr, 10);
90 }
91
92 bool
93 Ssl::CertValidationMsg::parseResponse(CertValidationResponse &resp)
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
108 void
109 Ssl::CertValidationMsg::tryParsingResponse(CertValidationResponse &resp)
110 {
111 std::vector<CertItem> certs;
112
113 const STACK_OF(X509) *peerCerts = PeerValidationCertificatesChain(resp.ssl);
114
115 const char *param = body.c_str();
116 while (*param) {
117 while (xisspace(*param)) param++;
118 if (! *param)
119 break;
120
121 size_t param_len = strcspn(param, "=\r\n");
122 if (param[param_len] != '=') {
123 throw TextException(ToSBuf("Missing parameter value: ", param), Here());
124 }
125 const char *value=param+param_len+1;
126
127 if (param_len > param_cert.length() &&
128 strncmp(param, param_cert.c_str(), param_cert.length()) == 0) {
129 CertItem ci;
130 ci.name.assign(param, param_len);
131 const auto x509 = ReadCertificate(ReadOnlyBioTiedTo(value));
132 ci.setCert(x509.get());
133 certs.push_back(ci);
134
135 const char *b = strstr(value, "-----END CERTIFICATE-----");
136 if (b == nullptr) {
137 throw TextException(ToSBuf("Missing END CERTIFICATE boundary: ", value), Here());
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
147 debugs(83, 5, "Returned value: " << std::string(param, param_len).c_str() << ": " <<
148 v.c_str());
149
150 int errorId = get_error_id(param, param_len);
151 Ssl::CertValidationResponse::RecvdError &currentItem = resp.getError(errorId);
152
153 if (param_len > param_error_name.length() &&
154 strncmp(param, param_error_name.c_str(), param_error_name.length()) == 0) {
155 currentItem.error_no = Ssl::GetErrorCode(v.c_str());
156 if (currentItem.error_no == SSL_ERROR_NONE) {
157 throw TextException(ToSBuf("Unknown TLS error name: ", v), Here());
158 }
159 } else if (param_len > param_error_reason.length() &&
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
165 if (X509 *cert = getCertByName(certs, v)) {
166 debugs(83, 6, "The certificate with id \"" << v << "\" found.");
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.
173 const int certId = get_error_id(v.c_str(), v.length());
174 debugs(83, 6, "Cert index in peer certificates list:" << certId);
175 //if certId is not correct sk_X509_value returns NULL
176 currentItem.setCert(sk_X509_value(peerCerts, certId));
177 }
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)) {
181 currentItem.error_depth = atoi(v.c_str());
182 } else {
183 throw TextException(ToSBuf("Unknown parameter name: ", std::string(param, param_len)), Here());
184 }
185
186 param = value + value_len;
187 }
188
189 /*Run through parsed errors to check for errors*/
190 typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
191 for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
192 if (i->error_no == SSL_ERROR_NONE) {
193 throw TextException(ToSBuf("Incomplete response; missing error name from error_id: ", i->id), Here());
194 }
195 }
196 }
197
198 X509 *
199 Ssl::CertValidationMsg::getCertByName(std::vector<CertItem> const &certs, std::string const & name)
200 {
201 typedef std::vector<CertItem>::const_iterator SVCI;
202 for (SVCI ci = certs.begin(); ci != certs.end(); ++ci) {
203 if (ci->name.compare(name) == 0)
204 return ci->cert.get();
205 }
206 return nullptr;
207 }
208
209 uint64_t
210 Ssl::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
216 Ssl::CertValidationResponse::RecvdError &
217 Ssl::CertValidationResponse::getError(int errorId)
218 {
219 typedef Ssl::CertValidationResponse::RecvdErrors::iterator SVCREI;
220 for (SVCREI i = errors.begin(); i != errors.end(); ++i) {
221 if (i->id == errorId)
222 return *i;
223 }
224 Ssl::CertValidationResponse::RecvdError errItem;
225 errItem.id = errorId;
226 errors.push_back(errItem);
227 return errors.back();
228 }
229
230 void
231 Ssl::CertValidationResponse::RecvdError::setCert(X509 *aCert)
232 {
233 cert.resetAndLock(aCert);
234 }
235
236 void
237 Ssl::CertValidationMsg::CertItem::setCert(X509 *aCert)
238 {
239 cert.resetAndLock(aCert);
240 }
241
242 const std::string Ssl::CertValidationMsg::code_cert_validate("cert_validate");
243 const std::string Ssl::CertValidationMsg::param_domain("domain");
244 const std::string Ssl::CertValidationMsg::param_cert("cert_");
245 const std::string Ssl::CertValidationMsg::param_error_name("error_name_");
246 const std::string Ssl::CertValidationMsg::param_error_reason("error_reason_");
247 const std::string Ssl::CertValidationMsg::param_error_cert("error_cert_");
248 const std::string Ssl::CertValidationMsg::param_error_depth("error_depth_");
249 const std::string Ssl::CertValidationMsg::param_proto_version("proto_version");
250 const std::string Ssl::CertValidationMsg::param_cipher("cipher");
251