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