2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
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.
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"
22 #include "ssl/ErrorDetailManager.h"
24 #if HAVE_GNUTLS_GNUTLS_H
25 #include <gnutls/gnutls.h>
33 // we use std::map to optimize search; TODO: Use std::unordered_map instead?
34 typedef std::map
<ErrorCode
, const char *> ErrorCodeNames
;
35 static const ErrorCodeNames TheErrorCodeNames
= {
36 { SQUID_TLS_ERR_ACCEPT
,
37 "SQUID_TLS_ERR_ACCEPT"
39 { SQUID_TLS_ERR_CONNECT
,
40 "SQUID_TLS_ERR_CONNECT"
42 { SQUID_X509_V_ERR_INFINITE_VALIDATION
,
43 "SQUID_X509_V_ERR_INFINITE_VALIDATION"
45 { SQUID_X509_V_ERR_CERT_CHANGE
,
46 "SQUID_X509_V_ERR_CERT_CHANGE"
48 { SQUID_X509_V_ERR_DOMAIN_MISMATCH
,
49 "SQUID_X509_V_ERR_DOMAIN_MISMATCH"
52 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT
,
53 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
55 { X509_V_ERR_UNABLE_TO_GET_CRL
,
56 "X509_V_ERR_UNABLE_TO_GET_CRL"
58 { X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE
,
59 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
61 { X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE
,
62 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
64 { X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY
,
65 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
67 { X509_V_ERR_CERT_SIGNATURE_FAILURE
,
68 "X509_V_ERR_CERT_SIGNATURE_FAILURE"
70 { X509_V_ERR_CRL_SIGNATURE_FAILURE
,
71 "X509_V_ERR_CRL_SIGNATURE_FAILURE"
73 { X509_V_ERR_CERT_NOT_YET_VALID
,
74 "X509_V_ERR_CERT_NOT_YET_VALID"
76 { X509_V_ERR_CERT_HAS_EXPIRED
,
77 "X509_V_ERR_CERT_HAS_EXPIRED"
79 { X509_V_ERR_CRL_NOT_YET_VALID
,
80 "X509_V_ERR_CRL_NOT_YET_VALID"
82 { X509_V_ERR_CRL_HAS_EXPIRED
,
83 "X509_V_ERR_CRL_HAS_EXPIRED"
85 { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD
,
86 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
88 { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD
,
89 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
91 { X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD
,
92 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
94 { X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD
,
95 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
97 { X509_V_ERR_OUT_OF_MEM
,
98 "X509_V_ERR_OUT_OF_MEM"
100 { X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
,
101 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
103 { X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN
,
104 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
106 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
,
107 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
109 { X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE
,
110 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
112 { X509_V_ERR_CERT_CHAIN_TOO_LONG
,
113 "X509_V_ERR_CERT_CHAIN_TOO_LONG"
115 { X509_V_ERR_CERT_REVOKED
,
116 "X509_V_ERR_CERT_REVOKED"
118 { X509_V_ERR_INVALID_CA
,
119 "X509_V_ERR_INVALID_CA"
121 { X509_V_ERR_PATH_LENGTH_EXCEEDED
,
122 "X509_V_ERR_PATH_LENGTH_EXCEEDED"
124 { X509_V_ERR_INVALID_PURPOSE
,
125 "X509_V_ERR_INVALID_PURPOSE"
127 { X509_V_ERR_CERT_UNTRUSTED
,
128 "X509_V_ERR_CERT_UNTRUSTED"
130 { X509_V_ERR_CERT_REJECTED
,
131 "X509_V_ERR_CERT_REJECTED"
133 { X509_V_ERR_SUBJECT_ISSUER_MISMATCH
,
134 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
136 { X509_V_ERR_AKID_SKID_MISMATCH
,
137 "X509_V_ERR_AKID_SKID_MISMATCH"
139 { X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH
,
140 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
142 { X509_V_ERR_KEYUSAGE_NO_CERTSIGN
,
143 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
145 #if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)
147 X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER
, // 33
148 "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER"
151 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
153 X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION
, // 34
154 "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
157 #if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)
159 X509_V_ERR_KEYUSAGE_NO_CRL_SIGN
, // 35
160 "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
163 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)
165 X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION
, // 36
166 "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
169 #if defined(X509_V_ERR_INVALID_NON_CA)
171 X509_V_ERR_INVALID_NON_CA
, // 37
172 "X509_V_ERR_INVALID_NON_CA"
175 #if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)
177 X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED
, // 38
178 "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
181 #if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE)
183 X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE
, // 39
184 "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
187 #if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)
189 X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED
, // 40
190 "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
193 #if defined(X509_V_ERR_INVALID_EXTENSION)
195 X509_V_ERR_INVALID_EXTENSION
, // 41
196 "X509_V_ERR_INVALID_EXTENSION"
199 #if defined(X509_V_ERR_INVALID_POLICY_EXTENSION)
201 X509_V_ERR_INVALID_POLICY_EXTENSION
, // 42
202 "X509_V_ERR_INVALID_POLICY_EXTENSION"
205 #if defined(X509_V_ERR_NO_EXPLICIT_POLICY)
207 X509_V_ERR_NO_EXPLICIT_POLICY
, // 43
208 "X509_V_ERR_NO_EXPLICIT_POLICY"
211 #if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
213 X509_V_ERR_DIFFERENT_CRL_SCOPE
, // 44
214 "X509_V_ERR_DIFFERENT_CRL_SCOPE"
217 #if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE)
219 X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE
, // 45
220 "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
223 #if defined(X509_V_ERR_UNNESTED_RESOURCE)
225 X509_V_ERR_UNNESTED_RESOURCE
, // 46
226 "X509_V_ERR_UNNESTED_RESOURCE"
229 #if defined(X509_V_ERR_PERMITTED_VIOLATION)
231 X509_V_ERR_PERMITTED_VIOLATION
, // 47
232 "X509_V_ERR_PERMITTED_VIOLATION"
235 #if defined(X509_V_ERR_EXCLUDED_VIOLATION)
237 X509_V_ERR_EXCLUDED_VIOLATION
, // 48
238 "X509_V_ERR_EXCLUDED_VIOLATION"
241 #if defined(X509_V_ERR_SUBTREE_MINMAX)
243 X509_V_ERR_SUBTREE_MINMAX
, // 49
244 "X509_V_ERR_SUBTREE_MINMAX"
247 { X509_V_ERR_APPLICATION_VERIFICATION
, // 50
248 "X509_V_ERR_APPLICATION_VERIFICATION"
250 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
252 X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE
, // 51
253 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
256 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX)
258 X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX
, // 52
259 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
262 #if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX)
264 X509_V_ERR_UNSUPPORTED_NAME_SYNTAX
, // 53
265 "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
268 #if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR)
270 X509_V_ERR_CRL_PATH_VALIDATION_ERROR
, // 54
271 "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
274 #if defined(X509_V_ERR_PATH_LOOP)
276 X509_V_ERR_PATH_LOOP
, // 55
277 "X509_V_ERR_PATH_LOOP"
280 #if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
282 X509_V_ERR_SUITE_B_INVALID_VERSION
, // 56
283 "X509_V_ERR_SUITE_B_INVALID_VERSION"
286 #if defined(X509_V_ERR_SUITE_B_INVALID_ALGORITHM)
288 X509_V_ERR_SUITE_B_INVALID_ALGORITHM
, // 57
289 "X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
292 #if defined(X509_V_ERR_SUITE_B_INVALID_CURVE)
294 X509_V_ERR_SUITE_B_INVALID_CURVE
, // 58
295 "X509_V_ERR_SUITE_B_INVALID_CURVE"
298 #if defined(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM)
300 X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM
, // 59
301 "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
304 #if defined(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED)
306 X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED
, // 60
307 "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
310 #if defined(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256)
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"
316 #if defined(X509_V_ERR_HOSTNAME_MISMATCH)
318 X509_V_ERR_HOSTNAME_MISMATCH
, // 62
319 "X509_V_ERR_HOSTNAME_MISMATCH"
322 #if defined(X509_V_ERR_EMAIL_MISMATCH)
324 X509_V_ERR_EMAIL_MISMATCH
, // 63
325 "X509_V_ERR_EMAIL_MISMATCH"
328 #if defined(X509_V_ERR_IP_ADDRESS_MISMATCH)
330 X509_V_ERR_IP_ADDRESS_MISMATCH
, // 64
331 "X509_V_ERR_IP_ADDRESS_MISMATCH"
334 #if defined(X509_V_ERR_DANE_NO_MATCH)
336 X509_V_ERR_DANE_NO_MATCH
, // 65
337 "X509_V_ERR_DANE_NO_MATCH"
340 #if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
342 X509_V_ERR_EE_KEY_TOO_SMALL
, // 66
343 "X509_V_ERR_EE_KEY_TOO_SMALL"
346 #if defined(X509_V_ERR_CA_KEY_TOO_SMALL)
348 X509_V_ERR_CA_KEY_TOO_SMALL
, // 67
349 "X509_V_ERR_CA_KEY_TOO_SMALL"
352 #if defined(X509_V_ERR_CA_MD_TOO_WEAK)
354 X509_V_ERR_CA_MD_TOO_WEAK
, // 68
355 "X509_V_ERR_CA_MD_TOO_WEAK"
358 #if defined(X509_V_ERR_INVALID_CALL)
360 X509_V_ERR_INVALID_CALL
, // 69
361 "X509_V_ERR_INVALID_CALL"
364 #if defined(X509_V_ERR_STORE_LOOKUP)
366 X509_V_ERR_STORE_LOOKUP
, // 70
367 "X509_V_ERR_STORE_LOOKUP"
370 #if defined(X509_V_ERR_NO_VALID_SCTS)
372 X509_V_ERR_NO_VALID_SCTS
, // 71
373 "X509_V_ERR_NO_VALID_SCTS"
376 #if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
378 X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION
, // 72
379 "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
382 #if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
384 X509_V_ERR_OCSP_VERIFY_NEEDED
, // 73
385 "X509_V_ERR_OCSP_VERIFY_NEEDED"
388 #if defined(X509_V_ERR_OCSP_VERIFY_FAILED)
390 X509_V_ERR_OCSP_VERIFY_FAILED
, // 74
391 "X509_V_ERR_OCSP_VERIFY_FAILED"
394 #if defined(X509_V_ERR_OCSP_CERT_UNKNOWN)
396 X509_V_ERR_OCSP_CERT_UNKNOWN
, // 75
397 "X509_V_ERR_OCSP_CERT_UNKNOWN"
404 #endif // USE_OPENSSL
407 } // namespace Security
410 Security::ErrorCodeFromName(const char *name
)
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
));
418 // redirector to support legacy error translations
419 TheErrorCodeByNameIndx
.insert(std::make_pair("SQUID_ERR_SSL_HANDSHAKE", SQUID_TLS_ERR_CONNECT
));
422 const auto it
= TheErrorCodeByNameIndx
.find(name
);
423 if (it
!= TheErrorCodeByNameIndx
.end())
430 Security::ErrorNameFromCode(const ErrorCode err
, const bool prefixRawCode
)
432 const auto it
= TheErrorCodeNames
.find(err
);
433 if (it
!= TheErrorCodeNames
.end())
436 static char tmpBuffer
[128];
437 snprintf(tmpBuffer
, sizeof(tmpBuffer
), "%s%d",
438 (prefixRawCode
? "SSL_ERR=" : ""), static_cast<int>(err
));
442 /* Security::ErrorDetail */
444 /// helper constructor implementing the logic shared by the two public ones
445 Security::ErrorDetail::ErrorDetail(const ErrorCode err
, const int aSysErrorNo
):
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
)
454 /// Extract and remember errors stored internally by the TLS library.
455 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.
462 // other libraries return errors explicitly instead of auto-storing them
466 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode
, const CertPointer
&cert
, const CertPointer
&broken
, const char *aReason
):
467 ErrorDetail(anErrorCode
, 0)
471 broken_cert
= broken
? broken
: cert
;
475 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode
, const int anIoErrorNo
, const int aSysErrorNo
):
476 ErrorDetail(anErrorCode
, aSysErrorNo
)
478 ioErrorNo
= anIoErrorNo
;
482 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode
, const LibErrorCode aLibErrorNo
, const int aSysErrorNo
):
483 ErrorDetail(anErrorCode
, aSysErrorNo
)
485 lib_error_no
= aLibErrorNo
;
490 Security::ErrorDetail::setPeerCertificate(const CertPointer
&cert
)
494 assert(!broken_cert
);
496 // unlike the constructor, the supplied certificate is not a broken_cert
500 Security::ErrorDetail::brief() const
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();
512 os
<< '+' << gnutls_strerror_name(lib_error_no
);
517 // TODO: Consider logging long but human-friendly names (e.g.,
518 // SSL_ERROR_SYSCALL).
520 os
<< "+TLS_IO_ERR=" << ioErrorNo
;
524 os
<< '+' << SysErrorDetail::Brief(sysErrorNo
);
528 os
<< "+broken_cert";
534 Security::ErrorDetail::verbose(const HttpRequestPointer
&request
) const
536 std::optional
<SBuf
> customFormat
;
538 if (const auto errorDetail
= Ssl::ErrorDetailsManager::GetInstance().findDetail(error_no
, request
))
539 customFormat
= errorDetail
->detail
;
543 auto format
= customFormat
? customFormat
->c_str() : "SSL handshake error (%err_name)";
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
)
553 remainder
= p
+ formattingCodeLen
;
559 /// textual representation of the subject of the broken certificate
561 Security::ErrorDetail::printSubject(std::ostream
&os
) const
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());
573 os
<< "[Not available]";
577 /// a helper class to print CNs extracted using Ssl::matchX509CommonNames()
578 class CommonNamesPrinter
581 explicit CommonNamesPrinter(std::ostream
&os
): os_(os
) {}
583 /// Ssl::matchX509CommonNames() visitor that reports the given name (if any)
584 static int PrintName(void *, ASN1_STRING
*);
586 /// whether any names have been printed so far
587 bool printed
= false;
590 void printName(const ASN1_STRING
*);
592 std::ostream
&os_
; ///< destination for printed names
596 CommonNamesPrinter::PrintName(void * const printer
, ASN1_STRING
* const name
)
599 static_cast<CommonNamesPrinter
*>(printer
)->printName(name
);
603 /// prints an HTML-quoted version of the given common name (as a part of the
604 /// printed names list)
606 CommonNamesPrinter::printName(const ASN1_STRING
* const name
)
608 if (name
&& name
->length
) {
612 // TODO: Convert html_quote() into an std::ostream manipulator accepting (buf, n).
613 SBuf
buf(reinterpret_cast<const char *>(name
->data
), name
->length
);
614 os_
<< html_quote(buf
.c_str());
619 #endif // USE_OPENSSL
621 /// a list of the broken certificates CN and alternate names
623 Security::ErrorDetail::printCommonName(std::ostream
&os
) const
626 if (broken_cert
.get()) {
627 CommonNamesPrinter
printer(os
);
628 Ssl::matchX509CommonNames(broken_cert
.get(), &printer
, printer
.PrintName
);
632 #endif // USE_OPENSSL
633 os
<< "[Not available]";
636 /// the issuer of the broken certificate
638 Security::ErrorDetail::printCaName(std::ostream
&os
) const
641 auto buf
= IssuerName(*broken_cert
);
642 if (!buf
.isEmpty()) {
643 // quote to avoid possible html code injection through
644 // certificate issuer subject
645 os
<< html_quote(buf
.c_str());
649 os
<< "[Not available]";
652 /// textual representation of the "not before" field of the broken certificate
654 Security::ErrorDetail::printNotBefore(std::ostream
&os
) const
657 if (broken_cert
.get()) {
658 if (const auto tm
= X509_getm_notBefore(broken_cert
.get())) {
659 // TODO: Add and use an ASN1_TIME printing operator instead.
660 static char tmpBuffer
[256]; // A temporary buffer
661 Ssl::asn1timeToString(tm
, tmpBuffer
, sizeof(tmpBuffer
));
666 #endif // USE_OPENSSL
667 os
<< "[Not available]";
670 /// textual representation of the "not after" field of the broken certificate
672 Security::ErrorDetail::printNotAfter(std::ostream
&os
) const
675 if (broken_cert
.get()) {
676 if (const auto tm
= X509_getm_notAfter(broken_cert
.get())) {
677 // XXX: Reduce code duplication.
678 static char tmpBuffer
[256]; // A temporary buffer
679 Ssl::asn1timeToString(tm
, tmpBuffer
, sizeof(tmpBuffer
));
684 #endif // USE_OPENSSL
685 os
<< "[Not available]";
688 /// textual representation of error_no
690 Security::ErrorDetail::printErrorCode(std::ostream
&os
) const
693 // try detailEntry first because it is faster
695 os
<< detailEntry
->name
;
699 os
<< ErrorNameFromCode(error_no
);
702 /// short description of error_no
704 Security::ErrorDetail::printErrorDescription(std::ostream
&os
) const
713 os
<< detailEntry
->descr
;
718 os
<< "[Not available]";
721 /// textual representation of lib_error_no
723 Security::ErrorDetail::printErrorLibError(std::ostream
&os
) const
725 if (errReason
.size() > 0)
727 else if (lib_error_no
)
728 os
<< ErrorString(lib_error_no
);
734 * Converts the code to a string value. Supported formatting codes are:
736 * Error meta information:
737 * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
738 * %ssl_error_descr: A short description of the SSL error
739 * %ssl_lib_error: human-readable low-level error string by ErrorString()
741 * Certificate information extracted from broken (not necessarily peer!) cert
742 * %ssl_cn: The comma-separated list of common and alternate names
743 * %ssl_subject: The certificate subject
744 * %ssl_ca_name: The certificate issuer name
745 * %ssl_notbefore: The certificate "not before" field
746 * %ssl_notafter: The certificate "not after" field
748 \returns the length of the code (the number of characters to be replaced by value)
749 \retval 0 for unsupported codes
752 Security::ErrorDetail::convertErrorCodeToDescription(const char * const code
, std::ostream
&os
) const
754 using PartDescriber
= void (ErrorDetail::*)(std::ostream
&os
) const;
755 static const std::map
<const char*, PartDescriber
> PartDescriberByCode
= {
756 {"ssl_subject", &ErrorDetail::printSubject
},
757 {"ssl_ca_name", &ErrorDetail::printCaName
},
758 {"ssl_cn", &ErrorDetail::printCommonName
},
759 {"ssl_notbefore", &ErrorDetail::printNotBefore
},
760 {"ssl_notafter", &ErrorDetail::printNotAfter
},
761 {"err_name", &ErrorDetail::printErrorCode
},
762 {"ssl_error_descr", &ErrorDetail::printErrorDescription
},
763 {"ssl_lib_error", &ErrorDetail::printErrorLibError
}
766 // We can refactor the map to find matches without looping, but that
767 // requires a "starts with" comparison function -- `code` length is unknown.
768 for (const auto &pair
: PartDescriberByCode
) {
769 const auto len
= strlen(pair
.first
);
770 if (strncmp(code
, pair
.first
, len
) == 0) {
771 const auto method
= pair
.second
;
777 // TODO: Support logformat %codes.