]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1996-2020 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 "errorpage.h" | |
11 | #include "fatal.h" | |
12 | #include "html_quote.h" | |
13 | #include "ssl/ErrorDetail.h" | |
14 | ||
15 | #include <climits> | |
16 | #include <map> | |
17 | ||
18 | struct SslErrorEntry { | |
19 | Security::ErrorCode value; | |
20 | const char *name; | |
21 | }; | |
22 | ||
23 | static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)"; | |
24 | //Use std::map to optimize search | |
25 | typedef std::map<Security::ErrorCode, const SslErrorEntry *> SslErrors; | |
26 | SslErrors TheSslErrors; | |
27 | ||
28 | static SslErrorEntry TheSslErrorArray[] = { | |
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 | }, | |
134 | #if defined(X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER) | |
135 | { | |
136 | X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER, //33 | |
137 | "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER" | |
138 | }, | |
139 | #endif | |
140 | #if defined(X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION) | |
141 | { | |
142 | X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION, //34 | |
143 | "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION" | |
144 | }, | |
145 | #endif | |
146 | #if defined(X509_V_ERR_KEYUSAGE_NO_CRL_SIGN) | |
147 | { | |
148 | X509_V_ERR_KEYUSAGE_NO_CRL_SIGN, //35 | |
149 | "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN" | |
150 | }, | |
151 | #endif | |
152 | #if defined(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION) | |
153 | { | |
154 | X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION, //36 | |
155 | "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION" | |
156 | }, | |
157 | #endif | |
158 | #if defined(X509_V_ERR_INVALID_NON_CA) | |
159 | { | |
160 | X509_V_ERR_INVALID_NON_CA, //37 | |
161 | "X509_V_ERR_INVALID_NON_CA" | |
162 | }, | |
163 | #endif | |
164 | #if defined(X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED) | |
165 | { | |
166 | X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED, //38 | |
167 | "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED" | |
168 | }, | |
169 | #endif | |
170 | #if defined(X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE) | |
171 | { | |
172 | X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE, //39 | |
173 | "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE" | |
174 | }, | |
175 | #endif | |
176 | #if defined(X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED) | |
177 | { | |
178 | X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED, //40 | |
179 | "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED" | |
180 | }, | |
181 | #endif | |
182 | #if defined(X509_V_ERR_INVALID_EXTENSION) | |
183 | { | |
184 | X509_V_ERR_INVALID_EXTENSION, //41 | |
185 | "X509_V_ERR_INVALID_EXTENSION" | |
186 | }, | |
187 | #endif | |
188 | #if defined(X509_V_ERR_INVALID_POLICY_EXTENSION) | |
189 | { | |
190 | X509_V_ERR_INVALID_POLICY_EXTENSION, //42 | |
191 | "X509_V_ERR_INVALID_POLICY_EXTENSION" | |
192 | }, | |
193 | #endif | |
194 | #if defined(X509_V_ERR_NO_EXPLICIT_POLICY) | |
195 | { | |
196 | X509_V_ERR_NO_EXPLICIT_POLICY, //43 | |
197 | "X509_V_ERR_NO_EXPLICIT_POLICY" | |
198 | }, | |
199 | #endif | |
200 | #if defined(X509_V_ERR_DIFFERENT_CRL_SCOPE) | |
201 | { | |
202 | X509_V_ERR_DIFFERENT_CRL_SCOPE, //44 | |
203 | "X509_V_ERR_DIFFERENT_CRL_SCOPE" | |
204 | }, | |
205 | #endif | |
206 | #if defined(X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE) | |
207 | { | |
208 | X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE, //45 | |
209 | "X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE" | |
210 | }, | |
211 | #endif | |
212 | #if defined(X509_V_ERR_UNNESTED_RESOURCE) | |
213 | { | |
214 | X509_V_ERR_UNNESTED_RESOURCE, //46 | |
215 | "X509_V_ERR_UNNESTED_RESOURCE" | |
216 | }, | |
217 | #endif | |
218 | #if defined(X509_V_ERR_PERMITTED_VIOLATION) | |
219 | { | |
220 | X509_V_ERR_PERMITTED_VIOLATION, //47 | |
221 | "X509_V_ERR_PERMITTED_VIOLATION" | |
222 | }, | |
223 | #endif | |
224 | #if defined(X509_V_ERR_EXCLUDED_VIOLATION) | |
225 | { | |
226 | X509_V_ERR_EXCLUDED_VIOLATION, //48 | |
227 | "X509_V_ERR_EXCLUDED_VIOLATION" | |
228 | }, | |
229 | #endif | |
230 | #if defined(X509_V_ERR_SUBTREE_MINMAX) | |
231 | { | |
232 | X509_V_ERR_SUBTREE_MINMAX, //49 | |
233 | "X509_V_ERR_SUBTREE_MINMAX" | |
234 | }, | |
235 | #endif | |
236 | #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE) | |
237 | { | |
238 | X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE, //51 | |
239 | "X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE" | |
240 | }, | |
241 | #endif | |
242 | #if defined(X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX) | |
243 | { | |
244 | X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX, //52 | |
245 | "X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX" | |
246 | }, | |
247 | #endif | |
248 | #if defined(X509_V_ERR_UNSUPPORTED_NAME_SYNTAX) | |
249 | { | |
250 | X509_V_ERR_UNSUPPORTED_NAME_SYNTAX, //53 | |
251 | "X509_V_ERR_UNSUPPORTED_NAME_SYNTAX" | |
252 | }, | |
253 | #endif | |
254 | #if defined(X509_V_ERR_CRL_PATH_VALIDATION_ERROR) | |
255 | { | |
256 | X509_V_ERR_CRL_PATH_VALIDATION_ERROR, //54 | |
257 | "X509_V_ERR_CRL_PATH_VALIDATION_ERROR" | |
258 | }, | |
259 | #endif | |
260 | { X509_V_ERR_APPLICATION_VERIFICATION, | |
261 | "X509_V_ERR_APPLICATION_VERIFICATION" | |
262 | }, | |
263 | { SSL_ERROR_NONE, "SSL_ERROR_NONE"}, | |
264 | {SSL_ERROR_NONE, NULL} | |
265 | }; | |
266 | ||
267 | static 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 | ||
292 | struct SslErrorAlias { | |
293 | const char *name; | |
294 | const Security::ErrorCode *errors; | |
295 | }; | |
296 | ||
297 | static const Security::ErrorCode hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE}; | |
298 | static const Security::ErrorCode notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE}; | |
299 | static const Security::ErrorCode domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE}; | |
300 | static const Security::ErrorCode certUntrusted[] = {X509_V_ERR_INVALID_CA, | |
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 | }; | |
307 | static const Security::ErrorCode certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE}; | |
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 | |
312 | // carefully stripping the scope prefix in Ssl::ParseErrorString(). | |
313 | static 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 | ||
327 | // Use std::map to optimize search. | |
328 | typedef std::map<std::string, const Security::ErrorCode *> SslErrorShortcuts; | |
329 | SslErrorShortcuts TheSslErrorShortcuts; | |
330 | ||
331 | static void loadSslErrorMap() | |
332 | { | |
333 | assert(TheSslErrors.empty()); | |
334 | for (int i = 0; TheSslErrorArray[i].name; ++i) { | |
335 | TheSslErrors[TheSslErrorArray[i].value] = &TheSslErrorArray[i]; | |
336 | } | |
337 | } | |
338 | ||
339 | static void loadSslErrorShortcutsMap() | |
340 | { | |
341 | assert(TheSslErrorShortcuts.empty()); | |
342 | for (int i = 0; TheSslErrorShortcutsArray[i].name; ++i) | |
343 | TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors; | |
344 | } | |
345 | ||
346 | Security::ErrorCode Ssl::GetErrorCode(const char *name) | |
347 | { | |
348 | //TODO: use a std::map? | |
349 | for (int i = 0; TheSslErrorArray[i].name != NULL; ++i) { | |
350 | if (strcmp(name, TheSslErrorArray[i].name) == 0) | |
351 | return TheSslErrorArray[i].value; | |
352 | } | |
353 | return SSL_ERROR_NONE; | |
354 | } | |
355 | ||
356 | bool | |
357 | Ssl::ParseErrorString(const char *name, Security::Errors &errors) | |
358 | { | |
359 | assert(name); | |
360 | ||
361 | const Security::ErrorCode ssl_error = GetErrorCode(name); | |
362 | if (ssl_error != SSL_ERROR_NONE) { | |
363 | errors.emplace(ssl_error); | |
364 | return true; | |
365 | } | |
366 | ||
367 | if (xisdigit(*name)) { | |
368 | const long int value = strtol(name, NULL, 0); | |
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); | |
374 | } | |
375 | ||
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... | |
382 | assert(it->second[0] != SSL_ERROR_NONE); | |
383 | for (int i = 0; it->second[i] != SSL_ERROR_NONE; ++i) { | |
384 | errors.emplace(it->second[i]); | |
385 | } | |
386 | return true; | |
387 | } | |
388 | ||
389 | fatalf("Unknown TLS error name '%s'", name); | |
390 | return false; // not reached | |
391 | } | |
392 | ||
393 | const char *Ssl::GetErrorName(Security::ErrorCode value) | |
394 | { | |
395 | if (TheSslErrors.empty()) | |
396 | loadSslErrorMap(); | |
397 | ||
398 | const SslErrors::const_iterator it = TheSslErrors.find(value); | |
399 | if (it != TheSslErrors.end()) | |
400 | return it->second->name; | |
401 | ||
402 | return NULL; | |
403 | } | |
404 | ||
405 | bool | |
406 | Ssl::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 | ||
415 | const char * | |
416 | Ssl::GetErrorDescr(Security::ErrorCode value) | |
417 | { | |
418 | return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value); | |
419 | } | |
420 | ||
421 | Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = { | |
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}, | |
428 | {"ssl_error_descr", &Ssl::ErrorDetail::err_descr}, | |
429 | {"ssl_lib_error", &Ssl::ErrorDetail::err_lib_error}, | |
430 | {NULL,NULL} | |
431 | }; | |
432 | ||
433 | /** | |
434 | * The subject of the current certification in text form | |
435 | */ | |
436 | const char *Ssl::ErrorDetail::subject() const | |
437 | { | |
438 | if (broken_cert.get()) { | |
439 | static char tmpBuffer[256]; // A temporary buffer | |
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 | } | |
445 | } | |
446 | return "[Not available]"; | |
447 | } | |
448 | ||
449 | // helper function to be used with Ssl::matchX509CommonNames | |
450 | static 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; | |
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 | } | |
460 | return 1; | |
461 | } | |
462 | ||
463 | /** | |
464 | * The list with certificates cn and alternate names | |
465 | */ | |
466 | const char *Ssl::ErrorDetail::cn() const | |
467 | { | |
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); | |
472 | if (tmpStr.size()) { | |
473 | // quote to avoid possible html code injection through | |
474 | // certificate subject | |
475 | return html_quote(tmpStr.termedBuf()); | |
476 | } | |
477 | } | |
478 | return "[Not available]"; | |
479 | } | |
480 | ||
481 | /** | |
482 | * The issuer name | |
483 | */ | |
484 | const char *Ssl::ErrorDetail::ca_name() const | |
485 | { | |
486 | if (broken_cert.get()) { | |
487 | static char tmpBuffer[256]; // A temporary buffer | |
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 | } | |
493 | } | |
494 | return "[Not available]"; | |
495 | } | |
496 | ||
497 | /** | |
498 | * The certificate "not before" field | |
499 | */ | |
500 | const char *Ssl::ErrorDetail::notbefore() const | |
501 | { | |
502 | if (broken_cert.get()) { | |
503 | if (const auto tm = X509_getm_notBefore(broken_cert.get())) { | |
504 | static char tmpBuffer[256]; // A temporary buffer | |
505 | Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer)); | |
506 | return tmpBuffer; | |
507 | } | |
508 | } | |
509 | return "[Not available]"; | |
510 | } | |
511 | ||
512 | /** | |
513 | * The certificate "not after" field | |
514 | */ | |
515 | const char *Ssl::ErrorDetail::notafter() const | |
516 | { | |
517 | if (broken_cert.get()) { | |
518 | if (const auto tm = X509_getm_notAfter(broken_cert.get())) { | |
519 | static char tmpBuffer[256]; // A temporary buffer | |
520 | Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer)); | |
521 | return tmpBuffer; | |
522 | } | |
523 | } | |
524 | return "[Not available]"; | |
525 | } | |
526 | ||
527 | /** | |
528 | * The string representation of the error_no | |
529 | */ | |
530 | const char *Ssl::ErrorDetail::err_code() const | |
531 | { | |
532 | static char tmpBuffer[64]; | |
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 | ||
542 | if (!err) { | |
543 | snprintf(tmpBuffer, 64, "%d", (int)error_no); | |
544 | err = tmpBuffer; | |
545 | } | |
546 | return err; | |
547 | } | |
548 | ||
549 | /** | |
550 | * A short description of the error_no | |
551 | */ | |
552 | const char *Ssl::ErrorDetail::err_descr() const | |
553 | { | |
554 | if (error_no == SSL_ERROR_NONE) | |
555 | return "[No Error]"; | |
556 | if (const char *err = detailEntry.descr.termedBuf()) | |
557 | return err; | |
558 | return "[Not available]"; | |
559 | } | |
560 | ||
561 | const char *Ssl::ErrorDetail::err_lib_error() const | |
562 | { | |
563 | if (errReason.size() > 0) | |
564 | return errReason.termedBuf(); | |
565 | else if (lib_error_no != SSL_ERROR_NONE) | |
566 | return Security::ErrorString(lib_error_no); | |
567 | else | |
568 | return "[No Error]"; | |
569 | } | |
570 | ||
571 | /** | |
572 | * Converts the code to a string value. Supported formating codes are: | |
573 | * | |
574 | * Error meta information: | |
575 | * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*) | |
576 | * %ssl_error_descr: A short description of the SSL error | |
577 | * %ssl_lib_error: human-readable low-level error string by Security::ErrorString() | |
578 | * | |
579 | * Certificate information extracted from broken (not necessarily peer!) cert | |
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 | |
585 | * | |
586 | \retval the length of the code (the number of characters will be replaced by value) | |
587 | */ | |
588 | int Ssl::ErrorDetail::convert(const char *code, const char **value) const | |
589 | { | |
590 | *value = "-"; | |
591 | for (int i=0; ErrorFormatingCodes[i].code!=NULL; ++i) { | |
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 | } | |
598 | } | |
599 | // TODO: Support logformat %codes. | |
600 | return 0; | |
601 | } | |
602 | ||
603 | /** | |
604 | * It uses the convert method to build the string errDetailStr using | |
605 | * a template message for the current SSL error. The template messages | |
606 | * can also contain normal error pages formating codes. | |
607 | * Currently the error template messages are hard-coded | |
608 | */ | |
609 | void Ssl::ErrorDetail::buildDetail() const | |
610 | { | |
611 | char const *s = NULL; | |
612 | char const *p; | |
613 | char const *t; | |
614 | int code_len = 0; | |
615 | ||
616 | if (ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request, detailEntry)) | |
617 | s = detailEntry.detail.termedBuf(); | |
618 | ||
619 | if (!s) | |
620 | s = SslErrorDetailDefaultStr; | |
621 | ||
622 | assert(s); | |
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 | ||
635 | const String &Ssl::ErrorDetail::toString() const | |
636 | { | |
637 | if (errDetailStr.size() == 0) | |
638 | buildDetail(); | |
639 | return errDetailStr; | |
640 | } | |
641 | ||
642 | Ssl::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) | |
643 | { | |
644 | if (cert) | |
645 | peer_cert.resetAndLock(cert); | |
646 | ||
647 | if (broken) | |
648 | broken_cert.resetAndLock(broken); | |
649 | else | |
650 | broken_cert.resetAndLock(cert); | |
651 | ||
652 | detailEntry.error_no = SSL_ERROR_NONE; | |
653 | } | |
654 | ||
655 | Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail) | |
656 | { | |
657 | error_no = anErrDetail.error_no; | |
658 | request = anErrDetail.request; | |
659 | ||
660 | if (anErrDetail.peer_cert.get()) { | |
661 | peer_cert.resetAndLock(anErrDetail.peer_cert.get()); | |
662 | } | |
663 | ||
664 | if (anErrDetail.broken_cert.get()) { | |
665 | broken_cert.resetAndLock(anErrDetail.broken_cert.get()); | |
666 | } | |
667 | ||
668 | detailEntry = anErrDetail.detailEntry; | |
669 | ||
670 | lib_error_no = anErrDetail.lib_error_no; | |
671 | } | |
672 |