ErrorDetail.cc
Go to the documentation of this file.
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 "base/IoManip.h"
11 #include "error/SysErrorDetail.h"
12 #include "html/Quoting.h"
13 #include "sbuf/SBuf.h"
14 #include "sbuf/Stream.h"
15 #include "security/Certificate.h"
16 #include "security/ErrorDetail.h"
17 #include "security/forward.h"
18 #include "security/Io.h"
19 #include "util.h"
20 
21 #if USE_OPENSSL
22 #include "ssl/ErrorDetailManager.h"
23 #elif HAVE_LIBGNUTLS
24 #if HAVE_GNUTLS_GNUTLS_H
25 #include <gnutls/gnutls.h>
26 #endif
27 #endif
28 #include <map>
29 #include <optional>
30 
31 namespace Security {
32 
33 // we use std::map to optimize search; TODO: Use std::unordered_map instead?
34 typedef std::map<ErrorCode, const char *> ErrorCodeNames;
37  "SQUID_TLS_ERR_ACCEPT"
38  },
40  "SQUID_TLS_ERR_CONNECT"
41  },
43  "SQUID_X509_V_ERR_INFINITE_VALIDATION"
44  },
46  "SQUID_X509_V_ERR_CERT_CHANGE"
47  },
49  "SQUID_X509_V_ERR_DOMAIN_MISMATCH"
50  },
51 #if USE_OPENSSL
52  { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
53  "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
54  },
55  { X509_V_ERR_UNABLE_TO_GET_CRL,
56  "X509_V_ERR_UNABLE_TO_GET_CRL"
57  },
58  { X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
59  "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
60  },
61  { X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
62  "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
63  },
64  { X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
65  "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
66  },
67  { X509_V_ERR_CERT_SIGNATURE_FAILURE,
68  "X509_V_ERR_CERT_SIGNATURE_FAILURE"
69  },
70  { X509_V_ERR_CRL_SIGNATURE_FAILURE,
71  "X509_V_ERR_CRL_SIGNATURE_FAILURE"
72  },
73  { X509_V_ERR_CERT_NOT_YET_VALID,
74  "X509_V_ERR_CERT_NOT_YET_VALID"
75  },
76  { X509_V_ERR_CERT_HAS_EXPIRED,
77  "X509_V_ERR_CERT_HAS_EXPIRED"
78  },
79  { X509_V_ERR_CRL_NOT_YET_VALID,
80  "X509_V_ERR_CRL_NOT_YET_VALID"
81  },
82  { X509_V_ERR_CRL_HAS_EXPIRED,
83  "X509_V_ERR_CRL_HAS_EXPIRED"
84  },
85  { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
86  "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
87  },
88  { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
89  "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
90  },
91  { X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
92  "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
93  },
94  { X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
95  "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
96  },
97  { X509_V_ERR_OUT_OF_MEM,
98  "X509_V_ERR_OUT_OF_MEM"
99  },
100  { X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
101  "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
102  },
103  { X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
104  "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
105  },
106  { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
107  "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
108  },
109  { X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
110  "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
111  },
112  { X509_V_ERR_CERT_CHAIN_TOO_LONG,
113  "X509_V_ERR_CERT_CHAIN_TOO_LONG"
114  },
115  { X509_V_ERR_CERT_REVOKED,
116  "X509_V_ERR_CERT_REVOKED"
117  },
118  { X509_V_ERR_INVALID_CA,
119  "X509_V_ERR_INVALID_CA"
120  },
121  { X509_V_ERR_PATH_LENGTH_EXCEEDED,
122  "X509_V_ERR_PATH_LENGTH_EXCEEDED"
123  },
124  { X509_V_ERR_INVALID_PURPOSE,
125  "X509_V_ERR_INVALID_PURPOSE"
126  },
127  { X509_V_ERR_CERT_UNTRUSTED,
128  "X509_V_ERR_CERT_UNTRUSTED"
129  },
130  { X509_V_ERR_CERT_REJECTED,
131  "X509_V_ERR_CERT_REJECTED"
132  },
133  { X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
134  "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
135  },
136  { X509_V_ERR_AKID_SKID_MISMATCH,
137  "X509_V_ERR_AKID_SKID_MISMATCH"
138  },
139  { X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
140  "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
141  },
142  { X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
143  "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
144  },
145 #if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)
146  {
147  X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, // 33
148  "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER"
149  },
150 #endif
151 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
152  {
153  X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, // 34
154  "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
155  },
156 #endif
157 #if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)
158  {
159  X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, // 35
160  "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
161  },
162 #endif
163 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)
164  {
165  X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, // 36
166  "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
167  },
168 #endif
169 #if defined(X509_V_ERR_INVALID_NON_CA)
170  {
171  X509_V_ERR_INVALID_NON_CA, // 37
172  "X509_V_ERR_INVALID_NON_CA"
173  },
174 #endif
175 #if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)
176  {
177  X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, // 38
178  "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
179  },
180 #endif
181 #if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE)
182  {
183  X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, // 39
184  "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
185  },
186 #endif
187 #if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)
188  {
189  X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, // 40
190  "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
191  },
192 #endif
193 #if defined(X509_V_ERR_INVALID_EXTENSION)
194  {
195  X509_V_ERR_INVALID_EXTENSION, // 41
196  "X509_V_ERR_INVALID_EXTENSION"
197  },
198 #endif
199 #if defined(X509_V_ERR_INVALID_POLICY_EXTENSION)
200  {
201  X509_V_ERR_INVALID_POLICY_EXTENSION, // 42
202  "X509_V_ERR_INVALID_POLICY_EXTENSION"
203  },
204 #endif
205 #if defined(X509_V_ERR_NO_EXPLICIT_POLICY)
206  {
207  X509_V_ERR_NO_EXPLICIT_POLICY, // 43
208  "X509_V_ERR_NO_EXPLICIT_POLICY"
209  },
210 #endif
211 #if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
212  {
213  X509_V_ERR_DIFFERENT_CRL_SCOPE, // 44
214  "X509_V_ERR_DIFFERENT_CRL_SCOPE"
215  },
216 #endif
217 #if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE)
218  {
219  X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, // 45
220  "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
221  },
222 #endif
223 #if defined(X509_V_ERR_UNNESTED_RESOURCE)
224  {
225  X509_V_ERR_UNNESTED_RESOURCE, // 46
226  "X509_V_ERR_UNNESTED_RESOURCE"
227  },
228 #endif
229 #if defined(X509_V_ERR_PERMITTED_VIOLATION)
230  {
231  X509_V_ERR_PERMITTED_VIOLATION, // 47
232  "X509_V_ERR_PERMITTED_VIOLATION"
233  },
234 #endif
235 #if defined(X509_V_ERR_EXCLUDED_VIOLATION)
236  {
237  X509_V_ERR_EXCLUDED_VIOLATION, // 48
238  "X509_V_ERR_EXCLUDED_VIOLATION"
239  },
240 #endif
241 #if defined(X509_V_ERR_SUBTREE_MINMAX)
242  {
243  X509_V_ERR_SUBTREE_MINMAX, // 49
244  "X509_V_ERR_SUBTREE_MINMAX"
245  },
246 #endif
247  { X509_V_ERR_APPLICATION_VERIFICATION, // 50
248  "X509_V_ERR_APPLICATION_VERIFICATION"
249  },
250 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
251  {
252  X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, // 51
253  "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
254  },
255 #endif
256 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX)
257  {
258  X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, // 52
259  "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
260  },
261 #endif
262 #if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX)
263  {
264  X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, // 53
265  "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
266  },
267 #endif
268 #if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR)
269  {
270  X509_V_ERR_CRL_PATH_VALIDATION_ERROR, // 54
271  "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
272  },
273 #endif
274 #if defined(X509_V_ERR_PATH_LOOP)
275  {
276  X509_V_ERR_PATH_LOOP, // 55
277  "X509_V_ERR_PATH_LOOP"
278  },
279 #endif
280 #if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
281  {
282  X509_V_ERR_SUITE_B_INVALID_VERSION, // 56
283  "X509_V_ERR_SUITE_B_INVALID_VERSION"
284  },
285 #endif
286 #if defined(X509_V_ERR_SUITE_B_INVALID_ALGORITHM)
287  {
288  X509_V_ERR_SUITE_B_INVALID_ALGORITHM, // 57
289  "X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
290  },
291 #endif
292 #if defined(X509_V_ERR_SUITE_B_INVALID_CURVE)
293  {
294  X509_V_ERR_SUITE_B_INVALID_CURVE, // 58
295  "X509_V_ERR_SUITE_B_INVALID_CURVE"
296  },
297 #endif
298 #if defined(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM)
299  {
300  X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM, // 59
301  "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
302  },
303 #endif
304 #if defined(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED)
305  {
306  X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED, // 60
307  "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
308  },
309 #endif
310 #if defined(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256)
311  {
312  X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256, // 61
313  "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256"
314  },
315 #endif
316 #if defined(X509_V_ERR_HOSTNAME_MISMATCH)
317  {
318  X509_V_ERR_HOSTNAME_MISMATCH, // 62
319  "X509_V_ERR_HOSTNAME_MISMATCH"
320  },
321 #endif
322 #if defined(X509_V_ERR_EMAIL_MISMATCH)
323  {
324  X509_V_ERR_EMAIL_MISMATCH, // 63
325  "X509_V_ERR_EMAIL_MISMATCH"
326  },
327 #endif
328 #if defined(X509_V_ERR_IP_ADDRESS_MISMATCH)
329  {
330  X509_V_ERR_IP_ADDRESS_MISMATCH, // 64
331  "X509_V_ERR_IP_ADDRESS_MISMATCH"
332  },
333 #endif
334 #if defined(X509_V_ERR_DANE_NO_MATCH)
335  {
336  X509_V_ERR_DANE_NO_MATCH, // 65
337  "X509_V_ERR_DANE_NO_MATCH"
338  },
339 #endif
340 #if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
341  {
342  X509_V_ERR_EE_KEY_TOO_SMALL, // 66
343  "X509_V_ERR_EE_KEY_TOO_SMALL"
344  },
345 #endif
346 #if defined(X509_V_ERR_CA_KEY_TOO_SMALL)
347  {
348  X509_V_ERR_CA_KEY_TOO_SMALL, // 67
349  "X509_V_ERR_CA_KEY_TOO_SMALL"
350  },
351 #endif
352 #if defined(X509_V_ERR_CA_MD_TOO_WEAK)
353  {
354  X509_V_ERR_CA_MD_TOO_WEAK, // 68
355  "X509_V_ERR_CA_MD_TOO_WEAK"
356  },
357 #endif
358 #if defined(X509_V_ERR_INVALID_CALL)
359  {
360  X509_V_ERR_INVALID_CALL, // 69
361  "X509_V_ERR_INVALID_CALL"
362  },
363 #endif
364 #if defined(X509_V_ERR_STORE_LOOKUP)
365  {
366  X509_V_ERR_STORE_LOOKUP, // 70
367  "X509_V_ERR_STORE_LOOKUP"
368  },
369 #endif
370 #if defined(X509_V_ERR_NO_VALID_SCTS)
371  {
372  X509_V_ERR_NO_VALID_SCTS, // 71
373  "X509_V_ERR_NO_VALID_SCTS"
374  },
375 #endif
376 #if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
377  {
378  X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION, // 72
379  "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
380  },
381 #endif
382 #if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
383  {
384  X509_V_ERR_OCSP_VERIFY_NEEDED, // 73
385  "X509_V_ERR_OCSP_VERIFY_NEEDED"
386  },
387 #endif
388 #if defined(X509_V_ERR_OCSP_VERIFY_FAILED)
389  {
390  X509_V_ERR_OCSP_VERIFY_FAILED, // 74
391  "X509_V_ERR_OCSP_VERIFY_FAILED"
392  },
393 #endif
394 #if defined(X509_V_ERR_OCSP_CERT_UNKNOWN)
395  {
396  X509_V_ERR_OCSP_CERT_UNKNOWN, // 75
397  "X509_V_ERR_OCSP_CERT_UNKNOWN"
398  },
399 #endif
400  {
401  SSL_ERROR_NONE,
402  "SSL_ERROR_NONE"
403  },
404 #endif // USE_OPENSSL
405 };
406 
407 } // namespace Security
408 
411 {
412  static auto TheCmp = [](const char *a, const char *b) {return strcmp(a, b) < 0;};
413  static std::map<const char *, ErrorCode, decltype(TheCmp)> TheErrorCodeByNameIndx(TheCmp);
414  if (TheErrorCodeByNameIndx.empty()) {
415  for (const auto &i: TheErrorCodeNames)
416  TheErrorCodeByNameIndx.insert(std::make_pair(i.second, i.first));
417 
418  // redirector to support legacy error translations
419  TheErrorCodeByNameIndx.insert(std::make_pair("SQUID_ERR_SSL_HANDSHAKE", SQUID_TLS_ERR_CONNECT));
420  }
421 
422  const auto it = TheErrorCodeByNameIndx.find(name);
423  if (it != TheErrorCodeByNameIndx.end())
424  return it->second;
425 
426  return 0;
427 }
428 
429 const char *
430 Security::ErrorNameFromCode(const ErrorCode err, const bool prefixRawCode)
431 {
432  const auto it = TheErrorCodeNames.find(err);
433  if (it != TheErrorCodeNames.end())
434  return it->second;
435 
436  static char tmpBuffer[128];
437  snprintf(tmpBuffer, sizeof(tmpBuffer), "%s%d",
438  (prefixRawCode ? "SSL_ERR=" : ""), static_cast<int>(err));
439  return tmpBuffer;
440 }
441 
442 /* Security::ErrorDetail */
443 
445 Security::ErrorDetail::ErrorDetail(const ErrorCode err, const int aSysErrorNo):
446  error_no(err),
447  // We could restrict errno(3) collection to cases where the TLS library
448  // explicitly talks about the errno being set, but correctly detecting those
449  // cases is difficult. We simplify in hope that all other cases will either
450  // have a useful errno or a zero errno.
451  sysErrorNo(aSysErrorNo)
452 {
453 #if USE_OPENSSL
454  if ((lib_error_no = ERR_get_error())) {
456  debugs(83, 7, "got 0x" << asHex(lib_error_no));
457  // more errors may be stacked
458  // TODO: Save/detail all stacked errors by always flushing stale ones.
459  ForgetErrors();
460  }
461 #else
462  // other libraries return errors explicitly instead of auto-storing them
463 #endif
464 }
465 
466 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const CertPointer &cert, const CertPointer &broken, const char *aReason):
467  ErrorDetail(anErrorCode, 0)
468 {
469  errReason = aReason;
470  peer_cert = cert;
471  broken_cert = broken ? broken : cert;
472 }
473 
474 #if USE_OPENSSL
475 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const int anIoErrorNo, const int aSysErrorNo):
476  ErrorDetail(anErrorCode, aSysErrorNo)
477 {
478  ioErrorNo = anIoErrorNo;
479 }
480 
481 #elif HAVE_LIBGNUTLS
482 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const LibErrorCode aLibErrorNo, const int aSysErrorNo):
483  ErrorDetail(anErrorCode, aSysErrorNo)
484 {
485  lib_error_no = aLibErrorNo;
486 }
487 #endif
488 
489 void
491 {
492  assert(cert);
493  assert(!peer_cert);
494  assert(!broken_cert);
495  peer_cert = cert;
496  // unlike the constructor, the supplied certificate is not a broken_cert
497 }
498 
499 SBuf
501 {
502  SBufStream os;
503 
504  printErrorCode(os);
505 
506  if (lib_error_no) {
507 #if USE_OPENSSL
508  // TODO: Log ERR_error_string_n() instead, despite length, whitespace?
509  // Example: `error:1408F09C:SSL routines:ssl3_get_record:http request`.
510  os << "+TLS_LIB_ERR=" << asHex(lib_error_no).upperCase();
511 #elif HAVE_LIBGNUTLS
512  os << '+' << gnutls_strerror_name(lib_error_no);
513 #endif
514  }
515 
516 #if USE_OPENSSL
517  // TODO: Consider logging long but human-friendly names (e.g.,
518  // SSL_ERROR_SYSCALL).
519  if (ioErrorNo)
520  os << "+TLS_IO_ERR=" << ioErrorNo;
521 #endif
522 
523  if (sysErrorNo) {
524  os << '+' << SysErrorDetail::Brief(sysErrorNo);
525  }
526 
527  if (broken_cert)
528  os << "+broken_cert";
529 
530  return os.buf();
531 }
532 
533 SBuf
535 {
536  std::optional<SBuf> customFormat;
537 #if USE_OPENSSL
538  if (const auto errorDetail = Ssl::ErrorDetailsManager::GetInstance().findDetail(error_no, request))
539  customFormat = errorDetail->detail;
540 #else
541  (void)request;
542 #endif
543  auto format = customFormat ? customFormat->c_str() : "SSL handshake error (%err_name)";
544 
545  SBufStream os;
546  assert(format);
547  auto remainder = format;
548  while (auto p = strchr(remainder, '%')) {
549  os.write(remainder, p - remainder);
550  const auto formattingCodeLen = convertErrorCodeToDescription(++p, os);
551  if (!formattingCodeLen)
552  os << '%';
553  remainder = p + formattingCodeLen;
554  }
555  os << remainder;
556  return os.buf();
557 }
558 
560 void
561 Security::ErrorDetail::printSubject(std::ostream &os) const
562 {
563  if (broken_cert) {
564  auto buf = SubjectName(*broken_cert);
565  if (!buf.isEmpty()) {
566  // TODO: Convert html_quote() into an std::ostream manipulator.
567  // quote to avoid possible html code injection through
568  // certificate subject
569  os << html_quote(buf.c_str());
570  return;
571  }
572  }
573  os << "[Not available]";
574 }
575 
576 #if USE_OPENSSL
579 {
580 public:
581  explicit CommonNamesPrinter(std::ostream &os): os_(os) {}
582 
584  mutable size_t printed = 0;
585 
586 protected:
587  /* GeneralNameMatcher API */
588  bool matchDomainName(const Dns::DomainName &) const override;
589  bool matchIp(const Ip::Address &) const override;
590 
591 private:
592  std::ostream &itemStream() const;
593 
594  std::ostream &os_;
595 };
596 
597 bool
599 {
600  // TODO: Convert html_quote() into an std::ostream manipulator accepting (buf, n).
601  itemStream() << html_quote(SBuf(domain).c_str());
602  return false; // keep going
603 }
604 
605 bool
607 {
608  char hostStr[MAX_IPSTRLEN];
609  (void)ip.toStr(hostStr, sizeof(hostStr)); // no html_quote() is needed; no brackets
610  itemStream().write(hostStr, strlen(hostStr));
611  return false; // keep going
612 }
613 
616 std::ostream &
618 {
619  if (printed++)
620  os_ << ", ";
621  return os_;
622 }
623 
624 #endif // USE_OPENSSL
625 
627 void
629 {
630 #if USE_OPENSSL
631  if (broken_cert.get()) {
632  CommonNamesPrinter printer(os);
633  (void)Ssl::HasMatchingSubjectName(*broken_cert, printer);
634  if (printer.printed)
635  return;
636  }
637 #endif // USE_OPENSSL
638  os << "[Not available]";
639 }
640 
642 void
643 Security::ErrorDetail::printCaName(std::ostream &os) const
644 {
645  if (broken_cert) {
646  auto buf = IssuerName(*broken_cert);
647  if (!buf.isEmpty()) {
648  // quote to avoid possible html code injection through
649  // certificate issuer subject
650  os << html_quote(buf.c_str());
651  return;
652  }
653  }
654  os << "[Not available]";
655 }
656 
658 void
660 {
661 #if USE_OPENSSL
662  if (broken_cert.get()) {
663  if (const auto tm = X509_getm_notBefore(broken_cert.get())) {
664  // TODO: Add and use an ASN1_TIME printing operator instead.
665  static char tmpBuffer[256]; // A temporary buffer
666  Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
667  os << tmpBuffer;
668  return;
669  }
670  }
671 #endif // USE_OPENSSL
672  os << "[Not available]";
673 }
674 
676 void
678 {
679 #if USE_OPENSSL
680  if (broken_cert.get()) {
681  if (const auto tm = X509_getm_notAfter(broken_cert.get())) {
682  // XXX: Reduce code duplication.
683  static char tmpBuffer[256]; // A temporary buffer
684  Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
685  os << tmpBuffer;
686  return;
687  }
688  }
689 #endif // USE_OPENSSL
690  os << "[Not available]";
691 }
692 
694 void
696 {
697 #if USE_OPENSSL
698  // try detailEntry first because it is faster
699  if (detailEntry) {
700  os << detailEntry->name;
701  return;
702  }
703 #endif
704  os << ErrorNameFromCode(error_no);
705 }
706 
708 void
710 {
711  if (!error_no) {
712  os << "[No Error]";
713  return;
714  }
715 
716 #if USE_OPENSSL
717  if (detailEntry) {
718  os << detailEntry->descr;
719  return;
720  }
721 #endif
722 
723  os << "[Not available]";
724 }
725 
727 void
729 {
730  if (errReason.size() > 0)
731  os << errReason;
732  else if (lib_error_no)
733  os << ErrorString(lib_error_no);
734  else
735  os << "[No Error]";
736 }
737 
756 size_t
757 Security::ErrorDetail::convertErrorCodeToDescription(const char * const code, std::ostream &os) const
758 {
759  using PartDescriber = void (ErrorDetail::*)(std::ostream &os) const;
760  static const std::map<const char*, PartDescriber> PartDescriberByCode = {
761  {"ssl_subject", &ErrorDetail::printSubject},
762  {"ssl_ca_name", &ErrorDetail::printCaName},
763  {"ssl_cn", &ErrorDetail::printCommonName},
764  {"ssl_notbefore", &ErrorDetail::printNotBefore},
765  {"ssl_notafter", &ErrorDetail::printNotAfter},
766  {"err_name", &ErrorDetail::printErrorCode},
767  {"ssl_error_descr", &ErrorDetail::printErrorDescription},
768  {"ssl_lib_error", &ErrorDetail::printErrorLibError}
769  };
770 
771  // We can refactor the map to find matches without looping, but that
772  // requires a "starts with" comparison function -- `code` length is unknown.
773  for (const auto &pair: PartDescriberByCode) {
774  const auto len = strlen(pair.first);
775  if (strncmp(code, pair.first, len) == 0) {
776  const auto method = pair.second;
777  (this->*method)(os);
778  return len;
779  }
780  }
781 
782  // TODO: Support logformat %codes.
783  return 0;
784 }
785 
ErrorCode ErrorCodeFromName(const char *name)
Definition: ErrorDetail.cc:410
void printSubject(std::ostream &os) const
textual representation of the subject of the broken certificate
Definition: ErrorDetail.cc:561
int ErrorCode
Squid-defined error code (<0), an error code returned by X.509 API, or zero.
Definition: forward.h:131
int asn1timeToString(ASN1_TIME *tm, char *buf, int len)
Definition: support.cc:243
bool HasMatchingSubjectName(X509 &, const GeneralNameMatcher &)
Definition: support.cc:302
prints X.509 names extracted using Ssl::HasMatchingSubjectName()
Definition: ErrorDetail.cc:578
@ SQUID_TLS_ERR_CONNECT
failure to establish a connection with a TLS server
Definition: forward.h:234
SBuf verbose(const HttpRequestPointer &) const override
Definition: ErrorDetail.cc:534
@ SQUID_X509_V_ERR_DOMAIN_MISMATCH
Definition: forward.h:238
void printCommonName(std::ostream &os) const
a list of the broken certificates CN and alternate names
Definition: ErrorDetail.cc:628
size_t printed
the number of names printed so far
Definition: ErrorDetail.cc:584
static const ErrorCodeNames TheErrorCodeNames
Definition: ErrorDetail.cc:35
Definition: SBuf.h:93
void printNotBefore(std::ostream &os) const
textual representation of the "not before" field of the broken certificate
Definition: ErrorDetail.cc:659
int matchDomainName(const char *h, const char *d, MatchDomainNameFlags flags)
Definition: Uri.cc:860
SBuf SubjectName(Certificate &)
The SubjectName field of the given certificate (if found) or an empty SBuf.
Definition: Certificate.cc:68
char * toStr(char *buf, const unsigned int blen, int force=AF_UNSPEC) const
Definition: Address.cc:812
#define MAX_IPSTRLEN
Length of buffer that needs to be allocated to old a null-terminated IP-string.
Definition: forward.h:25
CertPointer broken_cert
A pointer to the broken certificate (peer or intermediate)
Definition: ErrorDetail.h:99
SBuf brief() const override
Definition: ErrorDetail.cc:500
#define X509_getm_notBefore
Definition: openssl.h:248
void printNotAfter(std::ostream &os) const
textual representation of the "not after" field of the broken certificate
Definition: ErrorDetail.cc:677
void setPeerCertificate(const CertPointer &)
Definition: ErrorDetail.cc:490
char * html_quote(const char *string)
Definition: Quoting.cc:42
ErrorDetail(ErrorCode err_no, const CertPointer &peer, const CertPointer &broken, const char *aReason=nullptr)
Definition: ErrorDetail.cc:466
AsHex< Integer > asHex(const Integer n)
a helper to ease AsHex object creation
Definition: IoManip.h:169
void printErrorLibError(std::ostream &os) const
textual representation of lib_error_no
Definition: ErrorDetail.cc:728
void ForgetErrors()
clear any errors that a TLS library has accumulated in its global storage
Definition: Io.cc:71
#define assert(EX)
Definition: assert.h:17
SBuf IssuerName(Certificate &)
The Issuer field of the given certificate (if found) or an empty SBuf.
Definition: Certificate.cc:28
void printCaName(std::ostream &os) const
the issuer of the broken certificate
Definition: ErrorDetail.cc:643
SBuf buf()
bytes written so far
Definition: Stream.h:41
bool matchDomainName(const Dns::DomainName &) const override
Definition: ErrorDetail.cc:598
void printErrorDescription(std::ostream &os) const
short description of error_no
Definition: ErrorDetail.cc:709
const char * ErrorNameFromCode(ErrorCode err, bool prefixRawCode=false)
Definition: ErrorDetail.cc:430
static SBuf Brief(int errorNo)
bool matchIp(const Ip::Address &) const override
Definition: ErrorDetail.cc:606
int code
Definition: smb-errors.c:145
#define X509_getm_notAfter
Definition: openssl.h:247
static ErrorDetailsManager & GetInstance()
Instance class.
CommonNamesPrinter(std::ostream &os)
Definition: ErrorDetail.cc:581
String errReason
a custom reason for the error
Definition: ErrorDetail.h:123
an algorithm for checking/testing/comparing X.509 certificate names
Definition: support.h:298
std::ostream & os_
destination for printed names
Definition: ErrorDetail.cc:594
@ SQUID_X509_V_ERR_CERT_CHANGE
Definition: forward.h:237
void printErrorCode(std::ostream &os) const
textual representation of error_no
Definition: ErrorDetail.cc:695
@ SQUID_TLS_ERR_ACCEPT
failure to accept a connection from a TLS client
Definition: forward.h:233
std::map< ErrorCode, const char * > ErrorCodeNames
Definition: ErrorDetail.cc:34
size_t convertErrorCodeToDescription(const char *code, std::ostream &os) const
Definition: ErrorDetail.cc:757
CertPointer peer_cert
A pointer to the peer certificate.
Definition: ErrorDetail.h:98
Network/connection security abstraction layer.
Definition: Connection.h:33
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition: forward.h:152
LibErrorCode lib_error_no
TLS library-reported non-validation error or zero;.
Definition: ErrorDetail.h:105
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
@ SQUID_X509_V_ERR_INFINITE_VALIDATION
Definition: forward.h:239
std::ostream & itemStream() const
Definition: ErrorDetail.cc:617
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:141

 

Introduction

Documentation

Support

Miscellaneous