]>
Commit | Line | Data |
---|---|---|
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 | 18 | struct SslErrorEntry { |
13cd7dee | 19 | Security::ErrorCode value; |
4d16918e | 20 | const char *name; |
4d16918e CT |
21 | }; |
22 | ||
8e9bae99 | 23 | static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)"; |
cf09bec7 | 24 | //Use std::map to optimize search |
13cd7dee | 25 | typedef std::map<Security::ErrorCode, const SslErrorEntry *> SslErrors; |
02259ff8 | 26 | SslErrors TheSslErrors; |
cf09bec7 | 27 | |
02259ff8 | 28 | static 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 |
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 | ||
cf1c09f6 CT |
292 | struct SslErrorAlias { |
293 | const char *name; | |
13cd7dee | 294 | const Security::ErrorCode *errors; |
cf1c09f6 CT |
295 | }; |
296 | ||
13cd7dee AJ |
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, | |
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 | 307 | static 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 |
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 | ||
7a957a93 | 327 | // Use std::map to optimize search. |
13cd7dee | 328 | typedef std::map<std::string, const Security::ErrorCode *> SslErrorShortcuts; |
cf1c09f6 CT |
329 | SslErrorShortcuts TheSslErrorShortcuts; |
330 | ||
02259ff8 | 331 | static 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 |
339 | static 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 | 346 | Security::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 |
356 | bool |
357 | Ssl::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 | 393 | const 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 |
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 | ||
cf09bec7 | 415 | const char * |
13cd7dee | 416 | Ssl::GetErrorDescr(Security::ErrorCode value) |
cf09bec7 | 417 | { |
02259ff8 | 418 | return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value); |
cf09bec7 CT |
419 | } |
420 | ||
e34763f4 | 421 | Ssl::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 | */ | |
436 | const 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 | |
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; | |
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 | */ | |
466 | const 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 | */ | |
484 | const 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 | */ | |
500 | const 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 | */ | |
515 | const 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 | */ | |
530 | const 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 | */ | |
552 | const 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 |
561 | const 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 | */ | |
588 | int 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 | */ | |
609 | void 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 |
635 | const String &Ssl::ErrorDetail::toString() const |
636 | { | |
b38b26cb | 637 | if (errDetailStr.size() == 0) |
4d16918e CT |
638 | buildDetail(); |
639 | return errDetailStr; | |
640 | } | |
641 | ||
13cd7dee | 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) |
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 | ||
655 | Ssl::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 |