]> git.ipfire.org Git - thirdparty/squid.git/blob - src/security/ErrorDetail.cc
Activate extra compiler checks (#667)
[thirdparty/squid.git] / src / security / ErrorDetail.cc
1 /*
2 * Copyright (C) 1996-2021 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 "error/SysErrorDetail.h"
11 #include "html_quote.h"
12 #include "sbuf/SBuf.h"
13 #include "sbuf/Stream.h"
14 #include "security/ErrorDetail.h"
15 #include "security/forward.h"
16 #include "security/Io.h"
17 #include "util.h"
18
19 #if USE_OPENSSL
20 #include "ssl/ErrorDetailManager.h"
21 #elif USE_GNUTLS
22 #if HAVE_GNUTLS_GNUTLS_H
23 #include <gnutls/gnutls.h>
24 #endif
25 #endif
26 #include <map>
27
28 namespace Security {
29
30 // we use std::map to optimize search; TODO: Use std::unordered_map instead?
31 typedef std::map<ErrorCode, const char *> ErrorCodeNames;
32 static const ErrorCodeNames TheErrorCodeNames = {
33 { SQUID_TLS_ERR_ACCEPT,
34 "SQUID_TLS_ERR_ACCEPT"
35 },
36 { SQUID_TLS_ERR_CONNECT,
37 "SQUID_TLS_ERR_CONNECT"
38 },
39 { SQUID_X509_V_ERR_INFINITE_VALIDATION,
40 "SQUID_X509_V_ERR_INFINITE_VALIDATION"
41 },
42 { SQUID_X509_V_ERR_CERT_CHANGE,
43 "SQUID_X509_V_ERR_CERT_CHANGE"
44 },
45 { SQUID_X509_V_ERR_DOMAIN_MISMATCH,
46 "SQUID_X509_V_ERR_DOMAIN_MISMATCH"
47 },
48 #if USE_OPENSSL
49 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
50 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
51 },
52 { X509_V_ERR_UNABLE_TO_GET_CRL,
53 "X509_V_ERR_UNABLE_TO_GET_CRL"
54 },
55 { X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
56 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
57 },
58 { X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
59 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
60 },
61 { X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
62 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
63 },
64 { X509_V_ERR_CERT_SIGNATURE_FAILURE,
65 "X509_V_ERR_CERT_SIGNATURE_FAILURE"
66 },
67 { X509_V_ERR_CRL_SIGNATURE_FAILURE,
68 "X509_V_ERR_CRL_SIGNATURE_FAILURE"
69 },
70 { X509_V_ERR_CERT_NOT_YET_VALID,
71 "X509_V_ERR_CERT_NOT_YET_VALID"
72 },
73 { X509_V_ERR_CERT_HAS_EXPIRED,
74 "X509_V_ERR_CERT_HAS_EXPIRED"
75 },
76 { X509_V_ERR_CRL_NOT_YET_VALID,
77 "X509_V_ERR_CRL_NOT_YET_VALID"
78 },
79 { X509_V_ERR_CRL_HAS_EXPIRED,
80 "X509_V_ERR_CRL_HAS_EXPIRED"
81 },
82 { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
83 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
84 },
85 { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
86 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
87 },
88 { X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
89 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
90 },
91 { X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
92 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
93 },
94 { X509_V_ERR_OUT_OF_MEM,
95 "X509_V_ERR_OUT_OF_MEM"
96 },
97 { X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
98 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
99 },
100 { X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
101 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
102 },
103 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
104 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
105 },
106 { X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
107 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
108 },
109 { X509_V_ERR_CERT_CHAIN_TOO_LONG,
110 "X509_V_ERR_CERT_CHAIN_TOO_LONG"
111 },
112 { X509_V_ERR_CERT_REVOKED,
113 "X509_V_ERR_CERT_REVOKED"
114 },
115 { X509_V_ERR_INVALID_CA,
116 "X509_V_ERR_INVALID_CA"
117 },
118 { X509_V_ERR_PATH_LENGTH_EXCEEDED,
119 "X509_V_ERR_PATH_LENGTH_EXCEEDED"
120 },
121 { X509_V_ERR_INVALID_PURPOSE,
122 "X509_V_ERR_INVALID_PURPOSE"
123 },
124 { X509_V_ERR_CERT_UNTRUSTED,
125 "X509_V_ERR_CERT_UNTRUSTED"
126 },
127 { X509_V_ERR_CERT_REJECTED,
128 "X509_V_ERR_CERT_REJECTED"
129 },
130 { X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
131 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
132 },
133 { X509_V_ERR_AKID_SKID_MISMATCH,
134 "X509_V_ERR_AKID_SKID_MISMATCH"
135 },
136 { X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
137 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
138 },
139 { X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
140 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
141 },
142 #if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)
143 {
144 X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, // 33
145 "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER"
146 },
147 #endif
148 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
149 {
150 X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, // 34
151 "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
152 },
153 #endif
154 #if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)
155 {
156 X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, // 35
157 "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
158 },
159 #endif
160 #if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)
161 {
162 X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, // 36
163 "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
164 },
165 #endif
166 #if defined(X509_V_ERR_INVALID_NON_CA)
167 {
168 X509_V_ERR_INVALID_NON_CA, // 37
169 "X509_V_ERR_INVALID_NON_CA"
170 },
171 #endif
172 #if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)
173 {
174 X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, // 38
175 "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
176 },
177 #endif
178 #if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE)
179 {
180 X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, // 39
181 "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
182 },
183 #endif
184 #if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)
185 {
186 X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, // 40
187 "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
188 },
189 #endif
190 #if defined(X509_V_ERR_INVALID_EXTENSION)
191 {
192 X509_V_ERR_INVALID_EXTENSION, // 41
193 "X509_V_ERR_INVALID_EXTENSION"
194 },
195 #endif
196 #if defined(X509_V_ERR_INVALID_POLICY_EXTENSION)
197 {
198 X509_V_ERR_INVALID_POLICY_EXTENSION, // 42
199 "X509_V_ERR_INVALID_POLICY_EXTENSION"
200 },
201 #endif
202 #if defined(X509_V_ERR_NO_EXPLICIT_POLICY)
203 {
204 X509_V_ERR_NO_EXPLICIT_POLICY, // 43
205 "X509_V_ERR_NO_EXPLICIT_POLICY"
206 },
207 #endif
208 #if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
209 {
210 X509_V_ERR_DIFFERENT_CRL_SCOPE, // 44
211 "X509_V_ERR_DIFFERENT_CRL_SCOPE"
212 },
213 #endif
214 #if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE)
215 {
216 X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, // 45
217 "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
218 },
219 #endif
220 #if defined(X509_V_ERR_UNNESTED_RESOURCE)
221 {
222 X509_V_ERR_UNNESTED_RESOURCE, // 46
223 "X509_V_ERR_UNNESTED_RESOURCE"
224 },
225 #endif
226 #if defined(X509_V_ERR_PERMITTED_VIOLATION)
227 {
228 X509_V_ERR_PERMITTED_VIOLATION, // 47
229 "X509_V_ERR_PERMITTED_VIOLATION"
230 },
231 #endif
232 #if defined(X509_V_ERR_EXCLUDED_VIOLATION)
233 {
234 X509_V_ERR_EXCLUDED_VIOLATION, // 48
235 "X509_V_ERR_EXCLUDED_VIOLATION"
236 },
237 #endif
238 #if defined(X509_V_ERR_SUBTREE_MINMAX)
239 {
240 X509_V_ERR_SUBTREE_MINMAX, // 49
241 "X509_V_ERR_SUBTREE_MINMAX"
242 },
243 #endif
244 { X509_V_ERR_APPLICATION_VERIFICATION, // 50
245 "X509_V_ERR_APPLICATION_VERIFICATION"
246 },
247 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
248 {
249 X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, // 51
250 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
251 },
252 #endif
253 #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX)
254 {
255 X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, // 52
256 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
257 },
258 #endif
259 #if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX)
260 {
261 X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, // 53
262 "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
263 },
264 #endif
265 #if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR)
266 {
267 X509_V_ERR_CRL_PATH_VALIDATION_ERROR, // 54
268 "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
269 },
270 #endif
271 #if defined(X509_V_ERR_PATH_LOOP)
272 {
273 X509_V_ERR_PATH_LOOP, // 55
274 "X509_V_ERR_PATH_LOOP"
275 },
276 #endif
277 #if defined(X509_V_ERR_SUITE_B_INVALID_VERSION)
278 {
279 X509_V_ERR_SUITE_B_INVALID_VERSION, // 56
280 "X509_V_ERR_SUITE_B_INVALID_VERSION"
281 },
282 #endif
283 #if defined(X509_V_ERR_SUITE_B_INVALID_ALGORITHM)
284 {
285 X509_V_ERR_SUITE_B_INVALID_ALGORITHM, // 57
286 "X509_V_ERR_SUITE_B_INVALID_ALGORITHM"
287 },
288 #endif
289 #if defined(X509_V_ERR_SUITE_B_INVALID_CURVE)
290 {
291 X509_V_ERR_SUITE_B_INVALID_CURVE, // 58
292 "X509_V_ERR_SUITE_B_INVALID_CURVE"
293 },
294 #endif
295 #if defined(X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM)
296 {
297 X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM, // 59
298 "X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM"
299 },
300 #endif
301 #if defined(X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED)
302 {
303 X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED, // 60
304 "X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED"
305 },
306 #endif
307 #if defined(X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256)
308 {
309 X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256, // 61
310 "X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256"
311 },
312 #endif
313 #if defined(X509_V_ERR_HOSTNAME_MISMATCH)
314 {
315 X509_V_ERR_HOSTNAME_MISMATCH, // 62
316 "X509_V_ERR_HOSTNAME_MISMATCH"
317 },
318 #endif
319 #if defined(X509_V_ERR_EMAIL_MISMATCH)
320 {
321 X509_V_ERR_EMAIL_MISMATCH, // 63
322 "X509_V_ERR_EMAIL_MISMATCH"
323 },
324 #endif
325 #if defined(X509_V_ERR_IP_ADDRESS_MISMATCH)
326 {
327 X509_V_ERR_IP_ADDRESS_MISMATCH, // 64
328 "X509_V_ERR_IP_ADDRESS_MISMATCH"
329 },
330 #endif
331 #if defined(X509_V_ERR_DANE_NO_MATCH)
332 {
333 X509_V_ERR_DANE_NO_MATCH, // 65
334 "X509_V_ERR_DANE_NO_MATCH"
335 },
336 #endif
337 #if defined(X509_V_ERR_EE_KEY_TOO_SMALL)
338 {
339 X509_V_ERR_EE_KEY_TOO_SMALL, // 66
340 "X509_V_ERR_EE_KEY_TOO_SMALL"
341 },
342 #endif
343 #if defined(X509_V_ERR_CA_KEY_TOO_SMALL)
344 {
345 X509_V_ERR_CA_KEY_TOO_SMALL, // 67
346 "X509_V_ERR_CA_KEY_TOO_SMALL"
347 },
348 #endif
349 #if defined(X509_V_ERR_CA_MD_TOO_WEAK)
350 {
351 X509_V_ERR_CA_MD_TOO_WEAK, // 68
352 "X509_V_ERR_CA_MD_TOO_WEAK"
353 },
354 #endif
355 #if defined(X509_V_ERR_INVALID_CALL)
356 {
357 X509_V_ERR_INVALID_CALL, // 69
358 "X509_V_ERR_INVALID_CALL"
359 },
360 #endif
361 #if defined(X509_V_ERR_STORE_LOOKUP)
362 {
363 X509_V_ERR_STORE_LOOKUP, // 70
364 "X509_V_ERR_STORE_LOOKUP"
365 },
366 #endif
367 #if defined(X509_V_ERR_NO_VALID_SCTS)
368 {
369 X509_V_ERR_NO_VALID_SCTS, // 71
370 "X509_V_ERR_NO_VALID_SCTS"
371 },
372 #endif
373 #if defined(X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION)
374 {
375 X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION, // 72
376 "X509_V_ERR_PROXY_SUBJECT_NAME_VIOLATION"
377 },
378 #endif
379 #if defined(X509_V_ERR_OCSP_VERIFY_NEEDED)
380 {
381 X509_V_ERR_OCSP_VERIFY_NEEDED, // 73
382 "X509_V_ERR_OCSP_VERIFY_NEEDED"
383 },
384 #endif
385 #if defined(X509_V_ERR_OCSP_VERIFY_FAILED)
386 {
387 X509_V_ERR_OCSP_VERIFY_FAILED, // 74
388 "X509_V_ERR_OCSP_VERIFY_FAILED"
389 },
390 #endif
391 #if defined(X509_V_ERR_OCSP_CERT_UNKNOWN)
392 {
393 X509_V_ERR_OCSP_CERT_UNKNOWN, // 75
394 "X509_V_ERR_OCSP_CERT_UNKNOWN"
395 },
396 #endif
397 {
398 SSL_ERROR_NONE,
399 "SSL_ERROR_NONE"
400 },
401 #endif // USE_OPENSSL
402 };
403
404 } // namespace Security
405
406 Security::ErrorCode
407 Security::ErrorCodeFromName(const char *name)
408 {
409 static auto TheCmp = [](const char *a, const char *b) {return strcmp(a, b) < 0;};
410 static std::map<const char *, ErrorCode, decltype(TheCmp)> TheErrorCodeByNameIndx(TheCmp);
411 if (TheErrorCodeByNameIndx.empty()) {
412 for (const auto &i: TheErrorCodeNames)
413 TheErrorCodeByNameIndx.insert(std::make_pair(i.second, i.first));
414
415 // redirector to support legacy error translations
416 TheErrorCodeByNameIndx.insert(std::make_pair("SQUID_ERR_SSL_HANDSHAKE", SQUID_TLS_ERR_CONNECT));
417 }
418
419 const auto it = TheErrorCodeByNameIndx.find(name);
420 if (it != TheErrorCodeByNameIndx.end())
421 return it->second;
422
423 return 0;
424 }
425
426 const char *
427 Security::ErrorNameFromCode(const ErrorCode err, const bool prefixRawCode)
428 {
429 const auto it = TheErrorCodeNames.find(err);
430 if (it != TheErrorCodeNames.end())
431 return it->second;
432
433 static char tmpBuffer[128];
434 snprintf(tmpBuffer, sizeof(tmpBuffer), "%s%d",
435 (prefixRawCode ? "SSL_ERR=" : ""), static_cast<int>(err));
436 return tmpBuffer;
437 }
438
439 /* Security::ErrorDetail */
440
441 /// helper constructor implementing the logic shared by the two public ones
442 Security::ErrorDetail::ErrorDetail(const ErrorCode err, const int aSysErrorNo):
443 error_no(err),
444 // We could restrict errno(3) collection to cases where the TLS library
445 // explicitly talks about the errno being set, but correctly detecting those
446 // cases is difficult. We simplify in hope that all other cases will either
447 // have a useful errno or a zero errno.
448 sysErrorNo(aSysErrorNo)
449 {
450 #if USE_OPENSSL
451 /// Extract and remember errors stored internally by the TLS library.
452 if ((lib_error_no = ERR_get_error())) {
453 debugs(83, 7, "got " << asHex(lib_error_no));
454 // more errors may be stacked
455 // TODO: Save/detail all stacked errors by always flushing stale ones.
456 ForgetErrors();
457 }
458 #else
459 // other libraries return errors explicitly instead of auto-storing them
460 #endif
461 }
462
463 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const CertPointer &cert, const CertPointer &broken, const char *aReason):
464 ErrorDetail(anErrorCode, 0)
465 {
466 errReason = aReason;
467 peer_cert = cert;
468 broken_cert = broken ? broken : cert;
469 }
470
471 #if USE_OPENSSL
472 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const int anIoErrorNo, const int aSysErrorNo):
473 ErrorDetail(anErrorCode, aSysErrorNo)
474 {
475 ioErrorNo = anIoErrorNo;
476 }
477
478 #elif USE_GNUTLS
479 Security::ErrorDetail::ErrorDetail(const ErrorCode anErrorCode, const LibErrorCode aLibErrorNo, const int aSysErrorNo):
480 ErrorDetail(anErrorCode, aSysErrorNo)
481 {
482 lib_error_no = aLibErrorNo;
483 }
484 #endif
485
486 void
487 Security::ErrorDetail::setPeerCertificate(const CertPointer &cert)
488 {
489 assert(cert);
490 assert(!peer_cert);
491 assert(!broken_cert);
492 peer_cert = cert;
493 // unlike the constructor, the supplied certificate is not a broken_cert
494 }
495
496 SBuf
497 Security::ErrorDetail::brief() const
498 {
499 SBuf buf(err_code()); // TODO: Upgrade err_code()/etc. to return SBuf.
500
501 if (lib_error_no) {
502 #if USE_OPENSSL
503 // TODO: Log ERR_error_string_n() instead, despite length, whitespace?
504 // Example: `error:1408F09C:SSL routines:ssl3_get_record:http request`.
505 buf.append(ToSBuf("+TLS_LIB_ERR=", std::hex, std::uppercase, lib_error_no));
506 #elif USE_GNUTLS
507 buf.append(ToSBuf("+", gnutls_strerror_name(lib_error_no)));
508 #endif
509 }
510
511 #if USE_OPENSSL
512 // TODO: Consider logging long but human-friendly names (e.g.,
513 // SSL_ERROR_SYSCALL).
514 if (ioErrorNo)
515 buf.append(ToSBuf("+TLS_IO_ERR=", ioErrorNo));
516 #endif
517
518 if (sysErrorNo) {
519 buf.append('+');
520 buf.append(SysErrorDetail::Brief(sysErrorNo));
521 }
522
523 if (broken_cert)
524 buf.append("+broken_cert");
525
526 return buf;
527 }
528
529 SBuf
530 Security::ErrorDetail::verbose(const HttpRequestPointer &request) const
531 {
532 char const *format = nullptr;
533 #if USE_OPENSSL
534 if (Ssl::ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request, detailEntry))
535 format = detailEntry.detail.termedBuf();
536 #else
537 (void)request;
538 #endif
539 if (!format)
540 format = "SSL handshake error (%err_name)";
541
542 SBuf errDetailStr;
543 assert(format);
544 auto remainder = format;
545 while (auto p = strchr(remainder, '%')) {
546 errDetailStr.append(remainder, p - remainder);
547 char const *converted = nullptr;
548 const auto formattingCodeLen = convert(++p, &converted);
549 if (formattingCodeLen)
550 errDetailStr.append(converted);
551 else
552 errDetailStr.append("%");
553 remainder = p + formattingCodeLen;
554 }
555 errDetailStr.append(remainder, strlen(remainder));
556 return errDetailStr;
557 }
558
559 /// textual representation of the subject of the broken certificate
560 const char *
561 Security::ErrorDetail::subject() const
562 {
563 #if USE_OPENSSL
564 if (broken_cert.get()) {
565 static char tmpBuffer[256]; // A temporary buffer
566 if (X509_NAME_oneline(X509_get_subject_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer))) {
567 // quote to avoid possible html code injection through
568 // certificate subject
569 return html_quote(tmpBuffer);
570 }
571 }
572 #endif // USE_OPENSSL
573 return "[Not available]";
574 }
575
576 #if USE_OPENSSL
577 /// helper function to collect CNs using Ssl::matchX509CommonNames()
578 static int
579 copy_cn(void *check_data, ASN1_STRING *cn_data)
580 {
581 const auto str = static_cast<String*>(check_data);
582 if (!str) // no data? abort
583 return 0;
584 if (cn_data && cn_data->length) {
585 if (str->size() > 0)
586 str->append(", ");
587 str->append(reinterpret_cast<const char *>(cn_data->data), cn_data->length);
588 }
589 return 1;
590 }
591 #endif // USE_OPENSSL
592
593 /// a list of the broken certificates CN and alternate names
594 const char *
595 Security::ErrorDetail::cn() const
596 {
597 #if USE_OPENSSL
598 if (broken_cert.get()) {
599 static String tmpStr;
600 tmpStr.clean();
601 Ssl::matchX509CommonNames(broken_cert.get(), &tmpStr, copy_cn);
602 if (tmpStr.size()) {
603 // quote to avoid possible HTML code injection through
604 // certificate subject
605 return html_quote(tmpStr.termedBuf());
606 }
607 }
608 #endif // USE_OPENSSL
609 return "[Not available]";
610 }
611
612 /// the issuer of the broken certificate
613 const char *
614 Security::ErrorDetail::ca_name() const
615 {
616 #if USE_OPENSSL
617 if (broken_cert.get()) {
618 static char tmpBuffer[256]; // A temporary buffer
619 if (X509_NAME_oneline(X509_get_issuer_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer))) {
620 // quote to avoid possible html code injection through
621 // certificate issuer subject
622 return html_quote(tmpBuffer);
623 }
624 }
625 #endif // USE_OPENSSL
626 return "[Not available]";
627 }
628
629 /// textual representation of the "not before" field of the broken certificate
630 const char *
631 Security::ErrorDetail::notbefore() const
632 {
633 #if USE_OPENSSL
634 if (broken_cert.get()) {
635 if (const auto tm = X509_getm_notBefore(broken_cert.get())) {
636 static char tmpBuffer[256]; // A temporary buffer
637 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
638 return tmpBuffer;
639 }
640 }
641 #endif // USE_OPENSSL
642 return "[Not available]";
643 }
644
645 /// textual representation of the "not after" field of the broken certificate
646 const char *
647 Security::ErrorDetail::notafter() const
648 {
649 #if USE_OPENSSL
650 if (broken_cert.get()) {
651 if (const auto tm = X509_getm_notAfter(broken_cert.get())) {
652 static char tmpBuffer[256]; // A temporary buffer
653 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
654 return tmpBuffer;
655 }
656 }
657 #endif // USE_OPENSSL
658 return "[Not available]";
659 }
660
661 /// textual representation of error_no
662 const char *
663 Security::ErrorDetail::err_code() const
664 {
665 #if USE_OPENSSL
666 // try detailEntry first because it is faster
667 if (const char *err = detailEntry.name.termedBuf())
668 return err;
669 #endif
670
671 return ErrorNameFromCode(error_no);
672 }
673
674 /// short description of error_no
675 const char *
676 Security::ErrorDetail::err_descr() const
677 {
678 if (!error_no)
679 return "[No Error]";
680 #if USE_OPENSSL
681 if (const char *err = detailEntry.descr.termedBuf())
682 return err;
683 #endif
684 return "[Not available]";
685 }
686
687 /// textual representation of lib_error_no
688 const char *
689 Security::ErrorDetail::err_lib_error() const
690 {
691 if (errReason.size() > 0)
692 return errReason.termedBuf();
693 else if (lib_error_no)
694 return ErrorString(lib_error_no);
695 else
696 return "[No Error]";
697 return "[Not available]";
698 }
699
700 /**
701 * Converts the code to a string value. Supported formatting codes are:
702 *
703 * Error meta information:
704 * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
705 * %ssl_error_descr: A short description of the SSL error
706 * %ssl_lib_error: human-readable low-level error string by ErrorString()
707 *
708 * Certificate information extracted from broken (not necessarily peer!) cert
709 * %ssl_cn: The comma-separated list of common and alternate names
710 * %ssl_subject: The certificate subject
711 * %ssl_ca_name: The certificate issuer name
712 * %ssl_notbefore: The certificate "not before" field
713 * %ssl_notafter: The certificate "not after" field
714 *
715 \returns the length of the code (the number of characters to be replaced by value)
716 \retval 0 for unsupported codes
717 */
718 size_t
719 Security::ErrorDetail::convert(const char *code, const char **value) const
720 {
721 typedef const char *(ErrorDetail::*PartDescriber)() const;
722 static const std::map<const char*, PartDescriber> PartDescriberByCode = {
723 {"ssl_subject", &ErrorDetail::subject},
724 {"ssl_ca_name", &ErrorDetail::ca_name},
725 {"ssl_cn", &ErrorDetail::cn},
726 {"ssl_notbefore", &ErrorDetail::notbefore},
727 {"ssl_notafter", &ErrorDetail::notafter},
728 {"err_name", &ErrorDetail::err_code},
729 {"ssl_error_descr", &ErrorDetail::err_descr},
730 {"ssl_lib_error", &ErrorDetail::err_lib_error}
731 };
732
733 for (const auto &pair: PartDescriberByCode) {
734 const auto len = strlen(pair.first);
735 if (strncmp(code, pair.first, len) == 0) {
736 const auto method = pair.second;
737 *value = (this->*method)();
738 return len;
739 }
740 }
741
742 // TODO: Support logformat %codes.
743 *value = ""; // unused with zero return
744 return 0;
745 }
746