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