]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/ErrorDetail.cc
author: Alex Rousskov <rousskov@measurement-factory.com>, Christos Tsantilas <chtsant...
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
1 #include "squid-old.h"
2 #include "errorpage.h"
3 #include "ssl/ErrorDetail.h"
4 #if HAVE_MAP
5 #include <map>
6 #endif
7
8 struct SslErrorEntry {
9 Ssl::ssl_error_t value;
10 const char *name;
11 };
12
13 static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)";
14 //Use std::map to optimize search
15 typedef std::map<Ssl::ssl_error_t, const SslErrorEntry *> SslErrors;
16 SslErrors TheSslErrors;
17
18 static SslErrorEntry TheSslErrorArray[] = {
19 {SQUID_X509_V_ERR_CERT_CHANGE,
20 "SQUID_X509_V_ERR_CERT_CHANGE"},
21 {SQUID_ERR_SSL_HANDSHAKE,
22 "SQUID_ERR_SSL_HANDSHAKE"},
23 {SQUID_X509_V_ERR_DOMAIN_MISMATCH,
24 "SQUID_X509_V_ERR_DOMAIN_MISMATCH"},
25 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
26 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT"},
27 {X509_V_ERR_UNABLE_TO_GET_CRL,
28 "X509_V_ERR_UNABLE_TO_GET_CRL"},
29 {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
30 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE"},
31 {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
32 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE"},
33 {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
34 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY"},
35 {X509_V_ERR_CERT_SIGNATURE_FAILURE,
36 "X509_V_ERR_CERT_SIGNATURE_FAILURE"},
37 {X509_V_ERR_CRL_SIGNATURE_FAILURE,
38 "X509_V_ERR_CRL_SIGNATURE_FAILURE"},
39 {X509_V_ERR_CERT_NOT_YET_VALID,
40 "X509_V_ERR_CERT_NOT_YET_VALID"},
41 {X509_V_ERR_CERT_HAS_EXPIRED,
42 "X509_V_ERR_CERT_HAS_EXPIRED"},
43 {X509_V_ERR_CRL_NOT_YET_VALID,
44 "X509_V_ERR_CRL_NOT_YET_VALID"},
45 {X509_V_ERR_CRL_HAS_EXPIRED,
46 "X509_V_ERR_CRL_HAS_EXPIRED"},
47 {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
48 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD"},
49 {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
50 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD"},
51 {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
52 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD"},
53 {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
54 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD"},
55 {X509_V_ERR_OUT_OF_MEM,
56 "X509_V_ERR_OUT_OF_MEM"},
57 {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
58 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT"},
59 {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
60 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN"},
61 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
62 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY"},
63 {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
64 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE"},
65 {X509_V_ERR_CERT_CHAIN_TOO_LONG,
66 "X509_V_ERR_CERT_CHAIN_TOO_LONG"},
67 {X509_V_ERR_CERT_REVOKED,
68 "X509_V_ERR_CERT_REVOKED"},
69 {X509_V_ERR_INVALID_CA,
70 "X509_V_ERR_INVALID_CA"},
71 {X509_V_ERR_PATH_LENGTH_EXCEEDED,
72 "X509_V_ERR_PATH_LENGTH_EXCEEDED"},
73 {X509_V_ERR_INVALID_PURPOSE,
74 "X509_V_ERR_INVALID_PURPOSE"},
75 {X509_V_ERR_CERT_UNTRUSTED,
76 "X509_V_ERR_CERT_UNTRUSTED"},
77 {X509_V_ERR_CERT_REJECTED,
78 "X509_V_ERR_CERT_REJECTED"},
79 {X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
80 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH"},
81 {X509_V_ERR_AKID_SKID_MISMATCH,
82 "X509_V_ERR_AKID_SKID_MISMATCH"},
83 {X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
84 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH"},
85 {X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
86 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN"},
87 {X509_V_ERR_APPLICATION_VERIFICATION,
88 "X509_V_ERR_APPLICATION_VERIFICATION"},
89 { SSL_ERROR_NONE, "SSL_ERROR_NONE"},
90 {SSL_ERROR_NONE, NULL}
91 };
92
93 struct SslErrorAlias {
94 const char *name;
95 const Ssl::ssl_error_t *errors;
96 };
97
98 static const Ssl::ssl_error_t hasExpired[] = {X509_V_ERR_CERT_HAS_EXPIRED, SSL_ERROR_NONE};
99 static const Ssl::ssl_error_t notYetValid[] = {X509_V_ERR_CERT_NOT_YET_VALID, SSL_ERROR_NONE};
100 static const Ssl::ssl_error_t domainMismatch[] = {SQUID_X509_V_ERR_DOMAIN_MISMATCH, SSL_ERROR_NONE};
101 static const Ssl::ssl_error_t certUntrusted[] = {X509_V_ERR_INVALID_CA,
102 X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
103 X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
104 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
105 X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
106 X509_V_ERR_CERT_UNTRUSTED, SSL_ERROR_NONE};
107 static const Ssl::ssl_error_t certSelfSigned[] = {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT, SSL_ERROR_NONE};
108
109 // The list of error name shortcuts for use with ssl_error acls.
110 // The keys without the "ssl::" scope prefix allow shorter error
111 // names within the SSL options scope. This is easier than
112 // carefully stripping the scope prefix in Ssl::ParseErrorString().
113 static SslErrorAlias TheSslErrorShortcutsArray[] = {
114 {"ssl::certHasExpired", hasExpired},
115 {"certHasExpired", hasExpired},
116 {"ssl::certNotYetValid", notYetValid},
117 {"certNotYetValid", notYetValid},
118 {"ssl::certDomainMismatch", domainMismatch},
119 {"certDomainMismatch", domainMismatch},
120 {"ssl::certUntrusted", certUntrusted},
121 {"certUntrusted", certUntrusted},
122 {"ssl::certSelfSigned", certSelfSigned},
123 {"certSelfSigned", certSelfSigned},
124 {NULL, NULL}
125 };
126
127 // Use std::map to optimize search.
128 typedef std::map<std::string, const Ssl::ssl_error_t *> SslErrorShortcuts;
129 SslErrorShortcuts TheSslErrorShortcuts;
130
131 static void loadSslErrorMap()
132 {
133 assert(TheSslErrors.empty());
134 for (int i = 0; TheSslErrorArray[i].name; ++i) {
135 TheSslErrors[TheSslErrorArray[i].value] = &TheSslErrorArray[i];
136 }
137 }
138
139 static void loadSslErrorShortcutsMap()
140 {
141 assert(TheSslErrorShortcuts.empty());
142 for (int i = 0; TheSslErrorShortcutsArray[i].name; i++)
143 TheSslErrorShortcuts[TheSslErrorShortcutsArray[i].name] = TheSslErrorShortcutsArray[i].errors;
144 }
145
146 Ssl::ssl_error_t Ssl::GetErrorCode(const char *name)
147 {
148 for (int i = 0; TheSslErrorArray[i].name != NULL; i++) {
149 if (strcmp(name, TheSslErrorArray[i].name) == 0)
150 return TheSslErrorArray[i].value;
151 }
152 return SSL_ERROR_NONE;
153 }
154
155 Ssl::Errors *
156 Ssl::ParseErrorString(const char *name)
157 {
158 assert(name);
159
160 const Ssl::ssl_error_t ssl_error = GetErrorCode(name);
161 if (ssl_error != SSL_ERROR_NONE)
162 return new Ssl::Errors(ssl_error);
163
164 if (xisdigit(*name)) {
165 const long int value = strtol(name, NULL, 0);
166 if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
167 return new Ssl::Errors(value);
168 fatalf("Too small or too bug SSL error code '%s'", name);
169 }
170
171 if (TheSslErrorShortcuts.empty())
172 loadSslErrorShortcutsMap();
173
174 const SslErrorShortcuts::const_iterator it = TheSslErrorShortcuts.find(name);
175 if (it != TheSslErrorShortcuts.end()) {
176 // Should not be empty...
177 assert(it->second[0] != SSL_ERROR_NONE);
178 Ssl::Errors *errors = new Ssl::Errors(it->second[0]);
179 for (int i =1; it->second[i] != SSL_ERROR_NONE; i++) {
180 errors->push_back_unique(it->second[i]);
181 }
182 return errors;
183 }
184
185 fatalf("Unknown SSL error name '%s'", name);
186 return NULL; // not reached
187 }
188
189 const char *Ssl::GetErrorName(Ssl::ssl_error_t value)
190 {
191 if (TheSslErrors.empty())
192 loadSslErrorMap();
193
194 const SslErrors::const_iterator it = TheSslErrors.find(value);
195 if (it != TheSslErrors.end())
196 return it->second->name;
197
198 return NULL;
199 }
200
201 const char *
202 Ssl::GetErrorDescr(Ssl::ssl_error_t value)
203 {
204 return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value);
205 }
206
207 Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
208 {"ssl_subject", &Ssl::ErrorDetail::subject},
209 {"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
210 {"ssl_cn", &Ssl::ErrorDetail::cn},
211 {"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
212 {"ssl_notafter", &Ssl::ErrorDetail::notafter},
213 {"err_name", &Ssl::ErrorDetail::err_code},
214 {"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
215 {"ssl_lib_error", &Ssl::ErrorDetail::err_lib_error},
216 {NULL,NULL}
217 };
218
219 /**
220 * The subject of the current certification in text form
221 */
222 const char *Ssl::ErrorDetail::subject() const
223 {
224 if (!broken_cert)
225 return "[Not available]";
226
227 static char tmpBuffer[256]; // A temporary buffer
228 X509_NAME_oneline(X509_get_subject_name(broken_cert.get()), tmpBuffer,
229 sizeof(tmpBuffer));
230 return tmpBuffer;
231 }
232
233 // helper function to be used with Ssl::matchX509CommonNames
234 static int copy_cn(void *check_data, ASN1_STRING *cn_data)
235 {
236 String *str = (String *)check_data;
237 if (!str) // no data? abort
238 return 0;
239 if (str->defined())
240 str->append(", ");
241 str->append((const char *)cn_data->data, cn_data->length);
242 return 1;
243 }
244
245 /**
246 * The list with certificates cn and alternate names
247 */
248 const char *Ssl::ErrorDetail::cn() const
249 {
250 if (!broken_cert)
251 return "[Not available]";
252
253 static String tmpStr; ///< A temporary string buffer
254 tmpStr.clean();
255 Ssl::matchX509CommonNames(broken_cert.get(), &tmpStr, copy_cn);
256 return tmpStr.termedBuf();
257 }
258
259 /**
260 * The issuer name
261 */
262 const char *Ssl::ErrorDetail::ca_name() const
263 {
264 if (!broken_cert)
265 return "[Not available]";
266
267 static char tmpBuffer[256]; // A temporary buffer
268 X509_NAME_oneline(X509_get_issuer_name(broken_cert.get()), tmpBuffer, sizeof(tmpBuffer));
269 return tmpBuffer;
270 }
271
272 /**
273 * The certificate "not before" field
274 */
275 const char *Ssl::ErrorDetail::notbefore() const
276 {
277 if (!broken_cert)
278 return "[Not available]";
279
280 static char tmpBuffer[256]; // A temporary buffer
281 ASN1_UTCTIME * tm = X509_get_notBefore(broken_cert.get());
282 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
283 return tmpBuffer;
284 }
285
286 /**
287 * The certificate "not after" field
288 */
289 const char *Ssl::ErrorDetail::notafter() const
290 {
291 if (!broken_cert)
292 return "[Not available]";
293
294 static char tmpBuffer[256]; // A temporary buffer
295 ASN1_UTCTIME * tm = X509_get_notAfter(broken_cert.get());
296 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
297 return tmpBuffer;
298 }
299
300 /**
301 * The string representation of the error_no
302 */
303 const char *Ssl::ErrorDetail::err_code() const
304 {
305 static char tmpBuffer[64];
306 // We can use the GetErrorName but using the detailEntry is faster,
307 // so try it first.
308 const char *err = detailEntry.name.termedBuf();
309
310 // error details not loaded yet or not defined in error_details.txt,
311 // try the GetErrorName...
312 if (!err)
313 err = GetErrorName(error_no);
314
315 if (!err) {
316 snprintf(tmpBuffer, 64, "%d", (int)error_no);
317 err = tmpBuffer;
318 }
319 return err;
320 }
321
322 /**
323 * A short description of the error_no
324 */
325 const char *Ssl::ErrorDetail::err_descr() const
326 {
327 if (error_no == SSL_ERROR_NONE)
328 return "[No Error]";
329 if (const char *err = detailEntry.descr.termedBuf())
330 return err;
331 return "[Not available]";
332 }
333
334 const char *Ssl::ErrorDetail::err_lib_error() const
335 {
336 if (lib_error_no != SSL_ERROR_NONE)
337 return ERR_error_string(lib_error_no, NULL);
338 else
339 return "[No Error]";
340 }
341
342 /**
343 * Converts the code to a string value. Supported formating codes are:
344 *
345 * Error meta information:
346 * %err_name: The name of a high-level SSL error (e.g., X509_V_ERR_*)
347 * %ssl_error_descr: A short description of the SSL error
348 * %ssl_lib_error: human-readable low-level error string by ERR_error_string(3SSL)
349 *
350 * Certificate information extracted from broken (not necessarily peer!) cert
351 * %ssl_cn: The comma-separated list of common and alternate names
352 * %ssl_subject: The certificate subject
353 * %ssl_ca_name: The certificate issuer name
354 * %ssl_notbefore: The certificate "not before" field
355 * %ssl_notafter: The certificate "not after" field
356 *
357 \retval the length of the code (the number of characters will be replaced by value)
358 */
359 int Ssl::ErrorDetail::convert(const char *code, const char **value) const
360 {
361 *value = "-";
362 for (int i=0; ErrorFormatingCodes[i].code!=NULL; i++) {
363 const int len = strlen(ErrorFormatingCodes[i].code);
364 if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
365 ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
366 *value = (this->*action)();
367 return len;
368 }
369 }
370 return 0;
371 }
372
373 /**
374 * It uses the convert method to build the string errDetailStr using
375 * a template message for the current SSL error. The template messages
376 * can also contain normal error pages formating codes.
377 * Currently the error template messages are hard-coded
378 */
379 void Ssl::ErrorDetail::buildDetail() const
380 {
381 char const *s = NULL;
382 char const *p;
383 char const *t;
384 int code_len = 0;
385
386 if (ErrorDetailsManager::GetInstance().getErrorDetail(error_no, request.raw(), detailEntry))
387 s = detailEntry.detail.termedBuf();
388
389 if (!s)
390 s = SslErrorDetailDefaultStr;
391
392 assert(s);
393 while ((p = strchr(s, '%'))) {
394 errDetailStr.append(s, p - s);
395 code_len = convert(++p, &t);
396 if (code_len)
397 errDetailStr.append(t);
398 else
399 errDetailStr.append("%");
400 s = p + code_len;
401 }
402 errDetailStr.append(s, strlen(s));
403 }
404
405 const String &Ssl::ErrorDetail::toString() const
406 {
407 if (!errDetailStr.defined())
408 buildDetail();
409 return errDetailStr;
410 }
411
412 Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert, X509 *broken): error_no (err_no), lib_error_no(SSL_ERROR_NONE)
413 {
414 if (cert)
415 peer_cert.resetAndLock(cert);
416
417 if (broken)
418 broken_cert.resetAndLock(broken);
419 else
420 broken_cert.resetAndLock(cert);
421
422 detailEntry.error_no = SSL_ERROR_NONE;
423 }
424
425 Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
426 {
427 error_no = anErrDetail.error_no;
428 request = anErrDetail.request;
429
430 if (anErrDetail.peer_cert.get()) {
431 peer_cert.resetAndLock(anErrDetail.peer_cert.get());
432 }
433
434 if (anErrDetail.broken_cert.get()) {
435 broken_cert.resetAndLock(anErrDetail.broken_cert.get());
436 }
437
438 detailEntry = anErrDetail.detailEntry;
439
440 lib_error_no = anErrDetail.lib_error_no;
441 }