]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/ErrorDetail.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
CommitLineData
bbc27441 1/*
77b1029d 2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
bbc27441
AJ
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
582c2af2 9#include "squid.h"
02259ff8 10#include "errorpage.h"
ed6e9fb9 11#include "fatal.h"
6feeb15f 12#include "html_quote.h"
4d16918e 13#include "ssl/ErrorDetail.h"
074d6a40 14
582c2af2 15#include <climits>
074d6a40 16#include <map>
4d16918e 17
dc49061a 18struct SslErrorEntry {
13cd7dee 19 Security::ErrorCode value;
4d16918e 20 const char *name;
4d16918e
CT
21};
22
8e9bae99 23static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)";
cf09bec7 24//Use std::map to optimize search
13cd7dee 25typedef std::map<Security::ErrorCode, const SslErrorEntry *> SslErrors;
02259ff8 26SslErrors TheSslErrors;
cf09bec7 27
02259ff8 28static SslErrorEntry TheSslErrorArray[] = {
f53969cc
SM
29 { SQUID_X509_V_ERR_INFINITE_VALIDATION,
30 "SQUID_X509_V_ERR_INFINITE_VALIDATION"
31 },
32 { SQUID_X509_V_ERR_CERT_CHANGE,
33 "SQUID_X509_V_ERR_CERT_CHANGE"
34 },
35 { SQUID_ERR_SSL_HANDSHAKE,
36 "SQUID_ERR_SSL_HANDSHAKE"
37 },
38 { SQUID_X509_V_ERR_DOMAIN_MISMATCH,
39 "SQUID_X509_V_ERR_DOMAIN_MISMATCH"
40 },
41 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
42 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"
43 },
44 { X509_V_ERR_UNABLE_TO_GET_CRL,
45 "X509_V_ERR_UNABLE_TO_GET_CRL"
46 },
47 { X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
48 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"
49 },
50 { X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
51 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"
52 },
53 { X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
54 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"
55 },
56 { X509_V_ERR_CERT_SIGNATURE_FAILURE,
57 "X509_V_ERR_CERT_SIGNATURE_FAILURE"
58 },
59 { X509_V_ERR_CRL_SIGNATURE_FAILURE,
60 "X509_V_ERR_CRL_SIGNATURE_FAILURE"
61 },
62 { X509_V_ERR_CERT_NOT_YET_VALID,
63 "X509_V_ERR_CERT_NOT_YET_VALID"
64 },
65 { X509_V_ERR_CERT_HAS_EXPIRED,
66 "X509_V_ERR_CERT_HAS_EXPIRED"
67 },
68 { X509_V_ERR_CRL_NOT_YET_VALID,
69 "X509_V_ERR_CRL_NOT_YET_VALID"
70 },
71 { X509_V_ERR_CRL_HAS_EXPIRED,
72 "X509_V_ERR_CRL_HAS_EXPIRED"
73 },
74 { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
75 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"
76 },
77 { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
78 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"
79 },
80 { X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
81 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"
82 },
83 { X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
84 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"
85 },
86 { X509_V_ERR_OUT_OF_MEM,
87 "X509_V_ERR_OUT_OF_MEM"
88 },
89 { X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
90 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"
91 },
92 { X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
93 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"
94 },
95 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
96 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"
97 },
98 { X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
99 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"
100 },
101 { X509_V_ERR_CERT_CHAIN_TOO_LONG,
102 "X509_V_ERR_CERT_CHAIN_TOO_LONG"
103 },
104 { X509_V_ERR_CERT_REVOKED,
105 "X509_V_ERR_CERT_REVOKED"
106 },
107 { X509_V_ERR_INVALID_CA,
108 "X509_V_ERR_INVALID_CA"
109 },
110 { X509_V_ERR_PATH_LENGTH_EXCEEDED,
111 "X509_V_ERR_PATH_LENGTH_EXCEEDED"
112 },
113 { X509_V_ERR_INVALID_PURPOSE,
114 "X509_V_ERR_INVALID_PURPOSE"
115 },
116 { X509_V_ERR_CERT_UNTRUSTED,
117 "X509_V_ERR_CERT_UNTRUSTED"
118 },
119 { X509_V_ERR_CERT_REJECTED,
120 "X509_V_ERR_CERT_REJECTED"
121 },
122 { X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
123 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"
124 },
125 { X509_V_ERR_AKID_SKID_MISMATCH,
126 "X509_V_ERR_AKID_SKID_MISMATCH"
127 },
128 { X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
129 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"
130 },
131 { X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
132 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"
133 },
7a62af61 134#if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER)
6265d341
A
135 {
136 X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, //33
137 "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER"
138 },
7a62af61
CT
139#endif
140#if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION)
6265d341
A
141 {
142 X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, //34
143 "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION"
144 },
7a62af61
CT
145#endif
146#if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN)
6265d341
A
147 {
148 X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, //35
149 "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN"
150 },
7a62af61
CT
151#endif
152#if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION)
6265d341
A
153 {
154 X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, //36
155 "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION"
156 },
7a62af61
CT
157#endif
158#if defined(X509_V_ERR_INVALID_NON_CA)
6265d341
A
159 {
160 X509_V_ERR_INVALID_NON_CA, //37
161 "X509_V_ERR_INVALID_NON_CA"
162 },
7a62af61
CT
163#endif
164#if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED)
6265d341
A
165 {
166 X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, //38
167 "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED"
168 },
7a62af61
CT
169#endif
170#if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE)
6265d341
A
171 {
172 X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, //39
173 "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE"
174 },
7a62af61
CT
175#endif
176#if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED)
6265d341
A
177 {
178 X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, //40
179 "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED"
180 },
7a62af61
CT
181#endif
182#if defined(X509_V_ERR_INVALID_EXTENSION)
6265d341
A
183 {
184 X509_V_ERR_INVALID_EXTENSION, //41
185 "X509_V_ERR_INVALID_EXTENSION"
186 },
7a62af61
CT
187#endif
188#if defined(X509_V_ERR_INVALID_POLICY_EXTENSION)
6265d341
A
189 {
190 X509_V_ERR_INVALID_POLICY_EXTENSION, //42
191 "X509_V_ERR_INVALID_POLICY_EXTENSION"
192 },
7a62af61
CT
193#endif
194#if defined(X509_V_ERR_NO_EXPLICIT_POLICY)
6265d341
A
195 {
196 X509_V_ERR_NO_EXPLICIT_POLICY, //43
197 "X509_V_ERR_NO_EXPLICIT_POLICY"
198 },
7a62af61
CT
199#endif
200#if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE)
6265d341
A
201 {
202 X509_V_ERR_DIFFERENT_CRL_SCOPE, //44
203 "X509_V_ERR_DIFFERENT_CRL_SCOPE"
204 },
7a62af61
CT
205#endif
206#if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE)
6265d341
A
207 {
208 X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, //45
209 "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE"
210 },
7a62af61
CT
211#endif
212#if defined(X509_V_ERR_UNNESTED_RESOURCE)
6265d341
A
213 {
214 X509_V_ERR_UNNESTED_RESOURCE, //46
215 "X509_V_ERR_UNNESTED_RESOURCE"
216 },
7a62af61
CT
217#endif
218#if defined(X509_V_ERR_PERMITTED_VIOLATION)
6265d341
A
219 {
220 X509_V_ERR_PERMITTED_VIOLATION, //47
221 "X509_V_ERR_PERMITTED_VIOLATION"
222 },
7a62af61
CT
223#endif
224#if defined(X509_V_ERR_EXCLUDED_VIOLATION)
6265d341
A
225 {
226 X509_V_ERR_EXCLUDED_VIOLATION, //48
227 "X509_V_ERR_EXCLUDED_VIOLATION"
228 },
7a62af61
CT
229#endif
230#if defined(X509_V_ERR_SUBTREE_MINMAX)
6265d341
A
231 {
232 X509_V_ERR_SUBTREE_MINMAX, //49
233 "X509_V_ERR_SUBTREE_MINMAX"
234 },
7a62af61
CT
235#endif
236#if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE)
6265d341
A
237 {
238 X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, //51
239 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE"
240 },
7a62af61
CT
241#endif
242#if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX)
6265d341
A
243 {
244 X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, //52
245 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX"
246 },
7a62af61
CT
247#endif
248#if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX)
6265d341
A
249 {
250 X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, //53
251 "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX"
252 },
7a62af61
CT
253#endif
254#if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR)
6265d341
A
255 {
256 X509_V_ERR_CRL_PATH_VALIDATION_ERROR, //54
257 "X509_V_ERR_CRL_PATH_VALIDATION_ERROR"
258 },
7a62af61 259#endif
f53969cc
SM
260 { X509_V_ERR_APPLICATION_VERIFICATION,
261 "X509_V_ERR_APPLICATION_VERIFICATION"
262 },
02259ff8
CT
263 { SSL_ERROR_NONE, "SSL_ERROR_NONE"},
264 {SSL_ERROR_NONE, NULL}
4d16918e
CT
265};
266
645deacc
CT
267static const char *OptionalSslErrors[] = {
268 "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER",
269 "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION",
270 "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN",
271 "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION",
272 "X509_V_ERR_INVALID_NON_CA",
273 "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED",
274 "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE",
275 "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED",
276 "X509_V_ERR_INVALID_EXTENSION",
277 "X509_V_ERR_INVALID_POLICY_EXTENSION",
278 "X509_V_ERR_NO_EXPLICIT_POLICY",
279 "X509_V_ERR_DIFFERENT_CRL_SCOPE",
280 "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE",
281 "X509_V_ERR_UNNESTED_RESOURCE",
282 "X509_V_ERR_PERMITTED_VIOLATION",
283 "X509_V_ERR_EXCLUDED_VIOLATION",
284 "X509_V_ERR_SUBTREE_MINMAX",
285 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE",
286 "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX",
287 "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX",
288 "X509_V_ERR_CRL_PATH_VALIDATION_ERROR",
289 NULL
290};
291
cf1c09f6
CT
292struct SslErrorAlias {
293 const char *name;
13cd7dee 294 const Security::ErrorCode *errors;
cf1c09f6
CT
295};
296
13cd7dee
AJ
297static const Security::ErrorCode hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE};
298static const Security::ErrorCode notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE};
299static const Security::ErrorCode domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE};
300static const Security::ErrorCode certUntrusted[] = {X509_V_ERR_INVALID_CA,
83317b32
SM
301 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
302 X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
303 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
304 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
305 X509_V_ERR_CERT_UNTRUSTED, SSL_ERROR_NONE
306 };
13cd7dee 307static const Security::ErrorCode certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE};
cf1c09f6
CT
308
309// The list of error name shortcuts for use with ssl_error acls.
310// The keys without the "ssl::" scope prefix allow shorter error
311// names within the SSL options scope. This is easier than
7a957a93 312// carefully stripping the scope prefix in Ssl::ParseErrorString().
cf1c09f6
CT
313static SslErrorAlias TheSslErrorShortcutsArray[] = {
314 {"ssl::certHasExpired", hasExpired},
315 {"certHasExpired", hasExpired},
316 {"ssl::certNotYetValid", notYetValid},
317 {"certNotYetValid", notYetValid},
318 {"ssl::certDomainMismatch", domainMismatch},
319 {"certDomainMismatch", domainMismatch},
320 {"ssl::certUntrusted", certUntrusted},
321 {"certUntrusted", certUntrusted},
322 {"ssl::certSelfSigned", certSelfSigned},
323 {"certSelfSigned", certSelfSigned},
324 {NULL, NULL}
325};
326
7a957a93 327// Use std::map to optimize search.
13cd7dee 328typedef std::map<std::string, const Security::ErrorCode *> SslErrorShortcuts;
cf1c09f6
CT
329SslErrorShortcuts TheSslErrorShortcuts;
330
02259ff8 331static void loadSslErrorMap()
cf09bec7 332{
02259ff8
CT
333 assert(TheSslErrors.empty());
334 for (int i = 0; TheSslErrorArray[i].name; ++i) {
335 TheSslErrors[TheSslErrorArray[i].value] = &TheSslErrorArray[i];
cf09bec7
CT
336 }
337}
338
cf1c09f6
CT
339static void loadSslErrorShortcutsMap()
340{
341 assert(TheSslErrorShortcuts.empty());
a38ec4b1 342 for (int i = 0; TheSslErrorShortcutsArray[i].name; ++i)
cf1c09f6
CT
343 TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors;
344}
345
13cd7dee 346Security::ErrorCode Ssl::GetErrorCode(const char *name)
02259ff8 347{
d7ae3534
FC
348 //TODO: use a std::map?
349 for (int i = 0; TheSslErrorArray[i].name != NULL; ++i) {
02259ff8
CT
350 if (strcmp(name, TheSslErrorArray[i].name) == 0)
351 return TheSslErrorArray[i].value;
352 }
353 return SSL_ERROR_NONE;
354}
355
83f8d8f9
AJ
356bool
357Ssl::ParseErrorString(const char *name, Security::Errors &errors)
4d16918e
CT
358{
359 assert(name);
360
13cd7dee 361 const Security::ErrorCode ssl_error = GetErrorCode(name);
83f8d8f9
AJ
362 if (ssl_error != SSL_ERROR_NONE) {
363 errors.emplace(ssl_error);
364 return true;
365 }
4d16918e
CT
366
367 if (xisdigit(*name)) {
368 const long int value = strtol(name, NULL, 0);
83f8d8f9
AJ
369 if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX) {
370 errors.emplace(value);
371 return true;
372 }
373 fatalf("Too small or too big TLS error code '%s'", name);
4d16918e
CT
374 }
375
cf1c09f6
CT
376 if (TheSslErrorShortcuts.empty())
377 loadSslErrorShortcutsMap();
378
379 const SslErrorShortcuts::const_iterator it = TheSslErrorShortcuts.find(name);
380 if (it != TheSslErrorShortcuts.end()) {
381 // Should not be empty...
87f237a9 382 assert(it->second[0] != SSL_ERROR_NONE);
83f8d8f9
AJ
383 for (int i = 0; it->second[i] != SSL_ERROR_NONE; ++i) {
384 errors.emplace(it->second[i]);
cf1c09f6 385 }
83f8d8f9 386 return true;
cf1c09f6
CT
387 }
388
83f8d8f9
AJ
389 fatalf("Unknown TLS error name '%s'", name);
390 return false; // not reached
4d16918e
CT
391}
392
13cd7dee 393const char *Ssl::GetErrorName(Security::ErrorCode value)
cf09bec7 394{
02259ff8
CT
395 if (TheSslErrors.empty())
396 loadSslErrorMap();
cf09bec7 397
02259ff8
CT
398 const SslErrors::const_iterator it = TheSslErrors.find(value);
399 if (it != TheSslErrors.end())
400 return it->second->name;
4d16918e
CT
401
402 return NULL;
403}
404
645deacc
CT
405bool
406Ssl::ErrorIsOptional(const char *name)
407{
408 for (int i = 0; OptionalSslErrors[i] != NULL; ++i) {
409 if (strcmp(name, OptionalSslErrors[i]) == 0)
410 return true;
411 }
412 return false;
413}
414
cf09bec7 415const char *
13cd7dee 416Ssl::GetErrorDescr(Security::ErrorCode value)
cf09bec7 417{
02259ff8 418 return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value);
cf09bec7
CT
419}
420
e34763f4 421Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
4d16918e
CT
422 {"ssl_subject", &Ssl::ErrorDetail::subject},
423 {"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
424 {"ssl_cn", &Ssl::ErrorDetail::cn},
425 {"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
426 {"ssl_notafter", &Ssl::ErrorDetail::notafter},
427 {"err_name", &Ssl::ErrorDetail::err_code},
cf09bec7 428 {"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
8e9bae99 429 {"ssl_lib_error", &Ssl::ErrorDetail::err_lib_error},
4d16918e
CT
430 {NULL,NULL}
431};
432
433/**
434 * The subject of the current certification in text form
435 */
436const char *Ssl::ErrorDetail::subject() const
437{
ed58b523
CT
438 if (broken_cert.get()) {
439 static char tmpBuffer[256]; // A temporary buffer
6feeb15f
CT
440 if (X509_NAME_oneline(X509_get_subject_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer))) {
441 // quote to avoid possible html code injection through
442 // certificate subject
443 return html_quote(tmpBuffer);
444 }
ed58b523
CT
445 }
446 return "[Not available]";
4d16918e
CT
447}
448
449// helper function to be used with Ssl::matchX509CommonNames
450static int copy_cn(void *check_data, ASN1_STRING *cn_data)
451{
452 String *str = (String *)check_data;
453 if (!str) // no data? abort
454 return 0;
ed58b523
CT
455 if (cn_data && cn_data->length) {
456 if (str->size() > 0)
457 str->append(", ");
458 str->append((const char *)cn_data->data, cn_data->length);
459 }
4d16918e
CT
460 return 1;
461}
462
463/**
464 * The list with certificates cn and alternate names
465 */
466const char *Ssl::ErrorDetail::cn() const
467{
ed58b523
CT
468 if (broken_cert.get()) {
469 static String tmpStr; ///< A temporary string buffer
470 tmpStr.clean();
471 Ssl::matchX509CommonNames(broken_cert.get(), &tmpStr, copy_cn);
6feeb15f
CT
472 if (tmpStr.size()) {
473 // quote to avoid possible html code injection through
474 // certificate subject
475 return html_quote(tmpStr.termedBuf());
476 }
ed58b523
CT
477 }
478 return "[Not available]";
4d16918e
CT
479}
480
481/**
482 * The issuer name
483 */
484const char *Ssl::ErrorDetail::ca_name() const
485{
ed58b523
CT
486 if (broken_cert.get()) {
487 static char tmpBuffer[256]; // A temporary buffer
6feeb15f
CT
488 if (X509_NAME_oneline(X509_get_issuer_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer))) {
489 // quote to avoid possible html code injection through
490 // certificate issuer subject
491 return html_quote(tmpBuffer);
492 }
ed58b523
CT
493 }
494 return "[Not available]";
4d16918e
CT
495}
496
497/**
498 * The certificate "not before" field
499 */
500const char *Ssl::ErrorDetail::notbefore() const
501{
ed58b523 502 if (broken_cert.get()) {
24b30fdc 503 if (const auto tm = X509_getm_notBefore(broken_cert.get())) {
ed58b523
CT
504 static char tmpBuffer[256]; // A temporary buffer
505 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
506 return tmpBuffer;
507 }
508 }
509 return "[Not available]";
4d16918e
CT
510}
511
512/**
513 * The certificate "not after" field
514 */
515const char *Ssl::ErrorDetail::notafter() const
516{
ed58b523 517 if (broken_cert.get()) {
24b30fdc 518 if (const auto tm = X509_getm_notAfter(broken_cert.get())) {
ed58b523
CT
519 static char tmpBuffer[256]; // A temporary buffer
520 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
521 return tmpBuffer;
522 }
523 }
524 return "[Not available]";
4d16918e
CT
525}
526
527/**
528 * The string representation of the error_no
529 */
530const char *Ssl::ErrorDetail::err_code() const
531{
8211c314 532 static char tmpBuffer[64];
02259ff8
CT
533 // We can use the GetErrorName but using the detailEntry is faster,
534 // so try it first.
535 const char *err = detailEntry.name.termedBuf();
536
537 // error details not loaded yet or not defined in error_details.txt,
538 // try the GetErrorName...
539 if (!err)
540 err = GetErrorName(error_no);
541
8211c314 542 if (!err) {
605b80ca 543 snprintf(tmpBuffer, 64, "%d", (int)error_no);
8211c314
CT
544 err = tmpBuffer;
545 }
4d16918e
CT
546 return err;
547}
548
cf09bec7
CT
549/**
550 * A short description of the error_no
551 */
552const char *Ssl::ErrorDetail::err_descr() const
553{
02259ff8
CT
554 if (error_no == SSL_ERROR_NONE)
555 return "[No Error]";
556 if (const char *err = detailEntry.descr.termedBuf())
cf09bec7
CT
557 return err;
558 return "[Not available]";
559}
560
8e9bae99
CT
561const char *Ssl::ErrorDetail::err_lib_error() const
562{
b38b26cb 563 if (errReason.size() > 0)
2cef0ca6
AR
564 return errReason.termedBuf();
565 else if (lib_error_no != SSL_ERROR_NONE)
ea574635 566 return Security::ErrorString(lib_error_no);
8e9bae99
CT
567 else
568 return "[No Error]";
569}
570
4d16918e 571/**
7a957a93 572 * Converts the code to a string value. Supported formating codes are:
ff3bc018
AR
573 *
574 * Error meta information:
8e9bae99 575 * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
cf09bec7 576 * %ssl_error_descr: A short description of the SSL error
ea574635 577 * %ssl_lib_error: human-readable low-level error string by Security::ErrorString()
ff3bc018
AR
578 *
579 * Certificate information extracted from broken (not necessarily peer!) cert
4d16918e
CT
580 * %ssl_cn: The comma-separated list of common and alternate names
581 * %ssl_subject: The certificate subject
582 * %ssl_ca_name: The certificate issuer name
583 * %ssl_notbefore: The certificate "not before" field
584 * %ssl_notafter: The certificate "not after" field
87f237a9 585 *
4d16918e
CT
586 \retval the length of the code (the number of characters will be replaced by value)
587*/
588int Ssl::ErrorDetail::convert(const char *code, const char **value) const
589{
590 *value = "-";
d7ae3534 591 for (int i=0; ErrorFormatingCodes[i].code!=NULL; ++i) {
4d16918e
CT
592 const int len = strlen(ErrorFormatingCodes[i].code);
593 if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
594 ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
595 *value = (this->*action)();
596 return len;
597 }
e34763f4 598 }
7e6eabbc 599 // TODO: Support logformat %codes.
4d16918e
CT
600 return 0;
601}
602
603/**
e34763f4
A
604 * It uses the convert method to build the string errDetailStr using
605 * a template message for the current SSL error. The template messages
4d16918e
CT
606 * can also contain normal error pages formating codes.
607 * Currently the error template messages are hard-coded
608 */
609void Ssl::ErrorDetail::buildDetail() const
610{
02259ff8 611 char const *s = NULL;
4d16918e
CT
612 char const *p;
613 char const *t;
614 int code_len = 0;
615
b248c2a3 616 if (ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request, detailEntry))
02259ff8
CT
617 s = detailEntry.detail.termedBuf();
618
619 if (!s)
620 s = SslErrorDetailDefaultStr;
621
8211c314 622 assert(s);
4d16918e
CT
623 while ((p = strchr(s, '%'))) {
624 errDetailStr.append(s, p - s);
625 code_len = convert(++p, &t);
626 if (code_len)
627 errDetailStr.append(t);
628 else
629 errDetailStr.append("%");
630 s = p + code_len;
631 }
632 errDetailStr.append(s, strlen(s));
633}
634
e34763f4
A
635const String &Ssl::ErrorDetail::toString() const
636{
b38b26cb 637 if (errDetailStr.size() == 0)
4d16918e
CT
638 buildDetail();
639 return errDetailStr;
640}
641
13cd7dee 642Ssl::ErrorDetail::ErrorDetail( Security::ErrorCode err_no, X509 *cert, X509 *broken, const char *aReason): error_no (err_no), lib_error_no(SSL_ERROR_NONE), errReason(aReason)
4d16918e 643{
8e9bae99 644 if (cert)
de878a55
CT
645 peer_cert.resetAndLock(cert);
646
647 if (broken)
648 broken_cert.resetAndLock(broken);
649 else
650 broken_cert.resetAndLock(cert);
8e9bae99 651
02259ff8 652 detailEntry.error_no = SSL_ERROR_NONE;
4d16918e
CT
653}
654
655Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
656{
657 error_no = anErrDetail.error_no;
02259ff8
CT
658 request = anErrDetail.request;
659
4d16918e 660 if (anErrDetail.peer_cert.get()) {
de878a55
CT
661 peer_cert.resetAndLock(anErrDetail.peer_cert.get());
662 }
663
664 if (anErrDetail.broken_cert.get()) {
665 broken_cert.resetAndLock(anErrDetail.broken_cert.get());
4d16918e 666 }
02259ff8
CT
667
668 detailEntry = anErrDetail.detailEntry;
8e9bae99
CT
669
670 lib_error_no = anErrDetail.lib_error_no;
4d16918e 671}
f53969cc 672