]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/ErrorDetail.cc
Do not reuse persistent connections for PUTs to avoid ERR_ZERO_SIZE_OBJECT.
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
CommitLineData
582c2af2 1#include "squid.h"
02259ff8 2#include "errorpage.h"
4d16918e 3#include "ssl/ErrorDetail.h"
cf09bec7
CT
4#if HAVE_MAP
5#include <map>
6#endif
582c2af2
FC
7#if HAVE_CLIMITS
8#include <climits>
9#endif
4d16918e 10
dc49061a 11struct SslErrorEntry {
461b9576 12 Ssl::ssl_error_t value;
4d16918e 13 const char *name;
4d16918e
CT
14};
15
8e9bae99 16static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)";
cf09bec7 17//Use std::map to optimize search
02259ff8
CT
18typedef std::map<Ssl::ssl_error_t, const SslErrorEntry *> SslErrors;
19SslErrors TheSslErrors;
cf09bec7 20
02259ff8 21static SslErrorEntry TheSslErrorArray[] = {
e7bcc25f 22 {SQUID_X509_V_ERR_CERT_CHANGE,
87f237a9 23 "SQUID_X509_V_ERR_CERT_CHANGE"},
8e9bae99 24 {SQUID_ERR_SSL_HANDSHAKE,
87f237a9 25 "SQUID_ERR_SSL_HANDSHAKE"},
7698a79c 26 {SQUID_X509_V_ERR_DOMAIN_MISMATCH,
ef5fbcff 27 "SQUID_X509_V_ERR_DOMAIN_MISMATCH"},
2c3d5aec 28 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
0db46e4f 29 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"},
2c3d5aec 30 {X509_V_ERR_UNABLE_TO_GET_CRL,
02259ff8 31 "X509_V_ERR_UNABLE_TO_GET_CRL"},
2c3d5aec 32 {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
02259ff8 33 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"},
2c3d5aec 34 {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
02259ff8 35 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"},
2c3d5aec 36 {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
02259ff8 37 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"},
2c3d5aec 38 {X509_V_ERR_CERT_SIGNATURE_FAILURE,
02259ff8 39 "X509_V_ERR_CERT_SIGNATURE_FAILURE"},
cf09bec7 40 {X509_V_ERR_CRL_SIGNATURE_FAILURE,
02259ff8 41 "X509_V_ERR_CRL_SIGNATURE_FAILURE"},
cf09bec7 42 {X509_V_ERR_CERT_NOT_YET_VALID,
02259ff8 43 "X509_V_ERR_CERT_NOT_YET_VALID"},
cf09bec7 44 {X509_V_ERR_CERT_HAS_EXPIRED,
02259ff8 45 "X509_V_ERR_CERT_HAS_EXPIRED"},
cf09bec7 46 {X509_V_ERR_CRL_NOT_YET_VALID,
02259ff8 47 "X509_V_ERR_CRL_NOT_YET_VALID"},
cf09bec7 48 {X509_V_ERR_CRL_HAS_EXPIRED,
02259ff8 49 "X509_V_ERR_CRL_HAS_EXPIRED"},
cf09bec7 50 {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
02259ff8 51 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"},
cf09bec7 52 {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
02259ff8 53 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"},
cf09bec7 54 {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
02259ff8 55 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"},
cf09bec7 56 {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
02259ff8 57 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"},
cf09bec7 58 {X509_V_ERR_OUT_OF_MEM,
02259ff8 59 "X509_V_ERR_OUT_OF_MEM"},
4d16918e 60 {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
02259ff8 61 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"},
cf09bec7 62 {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
02259ff8 63 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"},
cf09bec7 64 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
02259ff8 65 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"},
cf09bec7 66 {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
02259ff8 67 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"},
cf09bec7 68 {X509_V_ERR_CERT_CHAIN_TOO_LONG,
02259ff8 69 "X509_V_ERR_CERT_CHAIN_TOO_LONG"},
cf09bec7 70 {X509_V_ERR_CERT_REVOKED,
02259ff8 71 "X509_V_ERR_CERT_REVOKED"},
cf09bec7 72 {X509_V_ERR_INVALID_CA,
02259ff8 73 "X509_V_ERR_INVALID_CA"},
cf09bec7 74 {X509_V_ERR_PATH_LENGTH_EXCEEDED,
02259ff8 75 "X509_V_ERR_PATH_LENGTH_EXCEEDED"},
cf09bec7 76 {X509_V_ERR_INVALID_PURPOSE,
02259ff8 77 "X509_V_ERR_INVALID_PURPOSE"},
cf09bec7 78 {X509_V_ERR_CERT_UNTRUSTED,
02259ff8 79 "X509_V_ERR_CERT_UNTRUSTED"},
cf09bec7 80 {X509_V_ERR_CERT_REJECTED,
02259ff8 81 "X509_V_ERR_CERT_REJECTED"},
cf09bec7 82 {X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
02259ff8 83 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"},
cf09bec7 84 {X509_V_ERR_AKID_SKID_MISMATCH,
02259ff8 85 "X509_V_ERR_AKID_SKID_MISMATCH"},
cf09bec7 86 {X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
02259ff8 87 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"},
cf09bec7 88 {X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
02259ff8 89 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"},
cf09bec7 90 {X509_V_ERR_APPLICATION_VERIFICATION,
02259ff8
CT
91 "X509_V_ERR_APPLICATION_VERIFICATION"},
92 { SSL_ERROR_NONE, "SSL_ERROR_NONE"},
93 {SSL_ERROR_NONE, NULL}
4d16918e
CT
94};
95
cf1c09f6
CT
96struct SslErrorAlias {
97 const char *name;
98 const Ssl::ssl_error_t *errors;
99};
100
101static const Ssl::ssl_error_t hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE};
102static const Ssl::ssl_error_t notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE};
103static const Ssl::ssl_error_t domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE};
104static const Ssl::ssl_error_t certUntrusted[] = {X509_V_ERR_INVALID_CA,
87f237a9
A
105 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
106 X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
107 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
108 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
109 X509_V_ERR_CERT_UNTRUSTED, SSL_ERROR_NONE
110 };
cf1c09f6
CT
111static const Ssl::ssl_error_t certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE};
112
113// The list of error name shortcuts for use with ssl_error acls.
114// The keys without the "ssl::" scope prefix allow shorter error
115// names within the SSL options scope. This is easier than
7a957a93 116// carefully stripping the scope prefix in Ssl::ParseErrorString().
cf1c09f6
CT
117static SslErrorAlias TheSslErrorShortcutsArray[] = {
118 {"ssl::certHasExpired", hasExpired},
119 {"certHasExpired", hasExpired},
120 {"ssl::certNotYetValid", notYetValid},
121 {"certNotYetValid", notYetValid},
122 {"ssl::certDomainMismatch", domainMismatch},
123 {"certDomainMismatch", domainMismatch},
124 {"ssl::certUntrusted", certUntrusted},
125 {"certUntrusted", certUntrusted},
126 {"ssl::certSelfSigned", certSelfSigned},
127 {"certSelfSigned", certSelfSigned},
128 {NULL, NULL}
129};
130
7a957a93 131// Use std::map to optimize search.
cf1c09f6
CT
132typedef std::map<std::string, const Ssl::ssl_error_t *> SslErrorShortcuts;
133SslErrorShortcuts TheSslErrorShortcuts;
134
02259ff8 135static void loadSslErrorMap()
cf09bec7 136{
02259ff8
CT
137 assert(TheSslErrors.empty());
138 for (int i = 0; TheSslErrorArray[i].name; ++i) {
139 TheSslErrors[TheSslErrorArray[i].value] = &TheSslErrorArray[i];
cf09bec7
CT
140 }
141}
142
cf1c09f6
CT
143static void loadSslErrorShortcutsMap()
144{
145 assert(TheSslErrorShortcuts.empty());
a38ec4b1 146 for (int i = 0; TheSslErrorShortcutsArray[i].name; ++i)
cf1c09f6
CT
147 TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors;
148}
149
02259ff8
CT
150Ssl::ssl_error_t Ssl::GetErrorCode(const char *name)
151{
d7ae3534
FC
152 //TODO: use a std::map?
153 for (int i = 0; TheSslErrorArray[i].name != NULL; ++i) {
02259ff8
CT
154 if (strcmp(name, TheSslErrorArray[i].name) == 0)
155 return TheSslErrorArray[i].value;
156 }
157 return SSL_ERROR_NONE;
158}
159
cf1c09f6 160Ssl::Errors *
5e430bf3 161Ssl::ParseErrorString(const char *name)
4d16918e
CT
162{
163 assert(name);
164
02259ff8
CT
165 const Ssl::ssl_error_t ssl_error = GetErrorCode(name);
166 if (ssl_error != SSL_ERROR_NONE)
cf1c09f6 167 return new Ssl::Errors(ssl_error);
4d16918e
CT
168
169 if (xisdigit(*name)) {
170 const long int value = strtol(name, NULL, 0);
171 if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
cf1c09f6 172 return new Ssl::Errors(value);
4d16918e
CT
173 fatalf("Too small or too bug SSL error code '%s'", name);
174 }
175
cf1c09f6
CT
176 if (TheSslErrorShortcuts.empty())
177 loadSslErrorShortcutsMap();
178
179 const SslErrorShortcuts::const_iterator it = TheSslErrorShortcuts.find(name);
180 if (it != TheSslErrorShortcuts.end()) {
181 // Should not be empty...
87f237a9 182 assert(it->second[0] != SSL_ERROR_NONE);
cf1c09f6 183 Ssl::Errors *errors = new Ssl::Errors(it->second[0]);
a38ec4b1 184 for (int i =1; it->second[i] != SSL_ERROR_NONE; ++i) {
cf1c09f6
CT
185 errors->push_back_unique(it->second[i]);
186 }
187 return errors;
188 }
189
4d16918e 190 fatalf("Unknown SSL error name '%s'", name);
cf1c09f6 191 return NULL; // not reached
4d16918e
CT
192}
193
02259ff8 194const char *Ssl::GetErrorName(Ssl::ssl_error_t value)
cf09bec7 195{
02259ff8
CT
196 if (TheSslErrors.empty())
197 loadSslErrorMap();
cf09bec7 198
02259ff8
CT
199 const SslErrors::const_iterator it = TheSslErrors.find(value);
200 if (it != TheSslErrors.end())
201 return it->second->name;
4d16918e
CT
202
203 return NULL;
204}
205
cf09bec7
CT
206const char *
207Ssl::GetErrorDescr(Ssl::ssl_error_t value)
208{
02259ff8 209 return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value);
cf09bec7
CT
210}
211
e34763f4 212Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
4d16918e
CT
213 {"ssl_subject", &Ssl::ErrorDetail::subject},
214 {"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
215 {"ssl_cn", &Ssl::ErrorDetail::cn},
216 {"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
217 {"ssl_notafter", &Ssl::ErrorDetail::notafter},
218 {"err_name", &Ssl::ErrorDetail::err_code},
cf09bec7 219 {"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
8e9bae99 220 {"ssl_lib_error", &Ssl::ErrorDetail::err_lib_error},
4d16918e
CT
221 {NULL,NULL}
222};
223
224/**
225 * The subject of the current certification in text form
226 */
227const char *Ssl::ErrorDetail::subject() const
228{
ff3bc018 229 if (!broken_cert)
4d16918e
CT
230 return "[Not available]";
231
232 static char tmpBuffer[256]; // A temporary buffer
ff3bc018 233 X509_NAME_oneline(X509_get_subject_name(broken_cert.get()), tmpBuffer,
e34763f4
A
234 sizeof(tmpBuffer));
235 return tmpBuffer;
4d16918e
CT
236}
237
238// helper function to be used with Ssl::matchX509CommonNames
239static int copy_cn(void *check_data, ASN1_STRING *cn_data)
240{
241 String *str = (String *)check_data;
242 if (!str) // no data? abort
243 return 0;
244 if (str->defined())
245 str->append(", ");
246 str->append((const char *)cn_data->data, cn_data->length);
247 return 1;
248}
249
250/**
251 * The list with certificates cn and alternate names
252 */
253const char *Ssl::ErrorDetail::cn() const
254{
ff3bc018 255 if (!broken_cert)
4d16918e
CT
256 return "[Not available]";
257
258 static String tmpStr; ///< A temporary string buffer
259 tmpStr.clean();
ff3bc018 260 Ssl::matchX509CommonNames(broken_cert.get(), &tmpStr, copy_cn);
4d16918e
CT
261 return tmpStr.termedBuf();
262}
263
264/**
265 * The issuer name
266 */
267const char *Ssl::ErrorDetail::ca_name() const
268{
ff3bc018 269 if (!broken_cert)
4d16918e
CT
270 return "[Not available]";
271
272 static char tmpBuffer[256]; // A temporary buffer
ff3bc018 273 X509_NAME_oneline(X509_get_issuer_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer));
4d16918e
CT
274 return tmpBuffer;
275}
276
277/**
278 * The certificate "not before" field
279 */
280const char *Ssl::ErrorDetail::notbefore() const
281{
ff3bc018 282 if (!broken_cert)
4d16918e
CT
283 return "[Not available]";
284
285 static char tmpBuffer[256]; // A temporary buffer
ff3bc018 286 ASN1_UTCTIME * tm = X509_get_notBefore(broken_cert.get());
4d16918e
CT
287 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
288 return tmpBuffer;
289}
290
291/**
292 * The certificate "not after" field
293 */
294const char *Ssl::ErrorDetail::notafter() const
295{
ff3bc018 296 if (!broken_cert)
4d16918e
CT
297 return "[Not available]";
298
299 static char tmpBuffer[256]; // A temporary buffer
ff3bc018 300 ASN1_UTCTIME * tm = X509_get_notAfter(broken_cert.get());
4d16918e
CT
301 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
302 return tmpBuffer;
303}
304
305/**
306 * The string representation of the error_no
307 */
308const char *Ssl::ErrorDetail::err_code() const
309{
8211c314 310 static char tmpBuffer[64];
02259ff8
CT
311 // We can use the GetErrorName but using the detailEntry is faster,
312 // so try it first.
313 const char *err = detailEntry.name.termedBuf();
314
315 // error details not loaded yet or not defined in error_details.txt,
316 // try the GetErrorName...
317 if (!err)
318 err = GetErrorName(error_no);
319
8211c314 320 if (!err) {
605b80ca 321 snprintf(tmpBuffer, 64, "%d", (int)error_no);
8211c314
CT
322 err = tmpBuffer;
323 }
4d16918e
CT
324 return err;
325}
326
cf09bec7
CT
327/**
328 * A short description of the error_no
329 */
330const char *Ssl::ErrorDetail::err_descr() const
331{
02259ff8
CT
332 if (error_no == SSL_ERROR_NONE)
333 return "[No Error]";
334 if (const char *err = detailEntry.descr.termedBuf())
cf09bec7
CT
335 return err;
336 return "[Not available]";
337}
338
8e9bae99
CT
339const char *Ssl::ErrorDetail::err_lib_error() const
340{
341 if (lib_error_no != SSL_ERROR_NONE)
342 return ERR_error_string(lib_error_no, NULL);
343 else
344 return "[No Error]";
345}
346
4d16918e 347/**
7a957a93 348 * Converts the code to a string value. Supported formating codes are:
ff3bc018
AR
349 *
350 * Error meta information:
8e9bae99 351 * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
cf09bec7 352 * %ssl_error_descr: A short description of the SSL error
ff3bc018
AR
353 * %ssl_lib_error: human-readable low-level error string by ERR_error_string(3SSL)
354 *
355 * Certificate information extracted from broken (not necessarily peer!) cert
4d16918e
CT
356 * %ssl_cn: The comma-separated list of common and alternate names
357 * %ssl_subject: The certificate subject
358 * %ssl_ca_name: The certificate issuer name
359 * %ssl_notbefore: The certificate "not before" field
360 * %ssl_notafter: The certificate "not after" field
87f237a9 361 *
4d16918e
CT
362 \retval the length of the code (the number of characters will be replaced by value)
363*/
364int Ssl::ErrorDetail::convert(const char *code, const char **value) const
365{
366 *value = "-";
d7ae3534 367 for (int i=0; ErrorFormatingCodes[i].code!=NULL; ++i) {
4d16918e
CT
368 const int len = strlen(ErrorFormatingCodes[i].code);
369 if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
370 ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
371 *value = (this->*action)();
372 return len;
373 }
e34763f4 374 }
4d16918e
CT
375 return 0;
376}
377
378/**
e34763f4
A
379 * It uses the convert method to build the string errDetailStr using
380 * a template message for the current SSL error. The template messages
4d16918e
CT
381 * can also contain normal error pages formating codes.
382 * Currently the error template messages are hard-coded
383 */
384void Ssl::ErrorDetail::buildDetail() const
385{
02259ff8 386 char const *s = NULL;
4d16918e
CT
387 char const *p;
388 char const *t;
389 int code_len = 0;
390
02259ff8
CT
391 if (ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request.raw(), detailEntry))
392 s = detailEntry.detail.termedBuf();
393
394 if (!s)
395 s = SslErrorDetailDefaultStr;
396
8211c314 397 assert(s);
4d16918e
CT
398 while ((p = strchr(s, '%'))) {
399 errDetailStr.append(s, p - s);
400 code_len = convert(++p, &t);
401 if (code_len)
402 errDetailStr.append(t);
403 else
404 errDetailStr.append("%");
405 s = p + code_len;
406 }
407 errDetailStr.append(s, strlen(s));
408}
409
e34763f4
A
410const String &Ssl::ErrorDetail::toString() const
411{
4d16918e
CT
412 if (!errDetailStr.defined())
413 buildDetail();
414 return errDetailStr;
415}
416
de878a55 417Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert, X509 *broken): error_no (err_no), lib_error_no(SSL_ERROR_NONE)
4d16918e 418{
8e9bae99 419 if (cert)
de878a55
CT
420 peer_cert.resetAndLock(cert);
421
422 if (broken)
423 broken_cert.resetAndLock(broken);
424 else
425 broken_cert.resetAndLock(cert);
8e9bae99 426
02259ff8 427 detailEntry.error_no = SSL_ERROR_NONE;
4d16918e
CT
428}
429
430Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
431{
432 error_no = anErrDetail.error_no;
02259ff8
CT
433 request = anErrDetail.request;
434
4d16918e 435 if (anErrDetail.peer_cert.get()) {
de878a55
CT
436 peer_cert.resetAndLock(anErrDetail.peer_cert.get());
437 }
438
439 if (anErrDetail.broken_cert.get()) {
440 broken_cert.resetAndLock(anErrDetail.broken_cert.get());
4d16918e 441 }
02259ff8
CT
442
443 detailEntry = anErrDetail.detailEntry;
8e9bae99
CT
444
445 lib_error_no = anErrDetail.lib_error_no;
4d16918e 446}