]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/ErrorDetail.cc
Cleanup: global/static function or methods should start from capital letter
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
1 #include "squid.h"
2 #include "ssl/ErrorDetail.h"
3 #if HAVE_MAP
4 #include <map>
5 #endif
6
7 struct SslErrorDetailEntry {
8 Ssl::ssl_error_t value;
9 const char *name;
10 const char *detail; ///< for error page %D macro expansion; may contain macros
11 const char *descr; ///< short error description (for use in debug messages or error pages)
12 };
13
14 static const char *SslErrorDetailDefaultStr = "SSL certificate validation error (%err_name): %ssl_subject";
15 //Use std::map to optimize search
16 typedef std::map<Ssl::ssl_error_t, const SslErrorDetailEntry *> SslErrorDetails;
17 SslErrorDetails TheSslDetail;
18
19 static SslErrorDetailEntry TheSslDetailArray[] = {
20 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
21 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
22 "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name",
23 "Unable to get issuer certificate"},
24 {X509_V_ERR_UNABLE_TO_GET_CRL,
25 "X509_V_ERR_UNABLE_TO_GET_CRL",
26 "%err_name: %ssl_error_descr: %ssl_subject",
27 "Unable to get certificate CRL"},
28 {X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE,
29 "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
30 "%err_name: %ssl_error_descr: %ssl_subject",
31 "Unable to decrypt certificate's signature"},
32 {X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE,
33 "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
34 "%err_name: %ssl_error_descr: %ssl_subject",
35 "Unable to decrypt CRL's signature"},
36 {X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY,
37 "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
38 "%err_name: Unable to decode issuer (CA) public key: %ssl_ca_name",
39 "Unable to decode issuer public key"},
40 {X509_V_ERR_CERT_SIGNATURE_FAILURE,
41 "X509_V_ERR_CERT_SIGNATURE_FAILURE",
42 "%err_name: %ssl_error_descr: %ssl_subject",
43 "Certificate signature failure"},
44 {X509_V_ERR_CRL_SIGNATURE_FAILURE,
45 "X509_V_ERR_CRL_SIGNATURE_FAILURE",
46 "%err_name: %ssl_error_descr: %ssl_subject",
47 "CRL signature failure"},
48 {X509_V_ERR_CERT_NOT_YET_VALID,
49 "X509_V_ERR_CERT_NOT_YET_VALID",
50 "%err_name: SSL Certficate is not valid before: %ssl_notbefore",
51 "Certificate is not yet valid"},
52 {X509_V_ERR_CERT_HAS_EXPIRED,
53 "X509_V_ERR_CERT_HAS_EXPIRED",
54 "%err_name: SSL Certificate expired on: %ssl_notafter",
55 "Certificate has expired"},
56 {X509_V_ERR_CRL_NOT_YET_VALID,
57 "X509_V_ERR_CRL_NOT_YET_VALID",
58 "%err_name: %ssl_error_descr: %ssl_subject",
59 "CRL is not yet valid"},
60 {X509_V_ERR_CRL_HAS_EXPIRED,
61 "X509_V_ERR_CRL_HAS_EXPIRED",
62 "%err_name: %ssl_error_descr: %ssl_subject",
63 "CRL has expired"},
64 {X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
65 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
66 "%err_name: SSL Certificate has invalid start date (the 'not before' field): %ssl_subject",
67 "Format error in certificate's notBefore field"},
68 {X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
69 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
70 "%err_name: SSL Certificate has invalid expiration date (the 'not after' field): %ssl_subject",
71 "Format error in certificate's notAfter field"},
72 {X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD,
73 "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
74 "%err_name: %ssl_error_descr: %ssl_subject",
75 "Format error in CRL's lastUpdate field"},
76 {X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD,
77 "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
78 "%err_name: %ssl_error_descr: %ssl_subject",
79 "Format error in CRL's nextUpdate field"},
80 {X509_V_ERR_OUT_OF_MEM,
81 "X509_V_ERR_OUT_OF_MEM",
82 "%err_name: %ssl_error_descr",
83 "Out of memory"},
84 {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
85 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
86 "%err_name: Self-signed SSL Certificate: %ssl_subject",
87 "Self signed certificate"},
88 {X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
89 "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
90 "%err_name: Self-signed SSL Certificate in chain: %ssl_subject",
91 "Self signed certificate in certificate chain"},
92 {X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
93 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
94 "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name",
95 "Unable to get local issuer certificate"},
96 {X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE,
97 "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
98 "%err_name: %ssl_error_descr: %ssl_subject",
99 "Unable to verify the first certificate"},
100 {X509_V_ERR_CERT_CHAIN_TOO_LONG,
101 "X509_V_ERR_CERT_CHAIN_TOO_LONG",
102 "%err_name: %ssl_error_descr: %ssl_subject",
103 "Certificate chain too long"},
104 {X509_V_ERR_CERT_REVOKED,
105 "X509_V_ERR_CERT_REVOKED",
106 "%err_name: %ssl_error_descr: %ssl_subject",
107 "Certificate revoked"},
108 {X509_V_ERR_INVALID_CA,
109 "X509_V_ERR_INVALID_CA",
110 "%err_name: %ssl_error_descr: %ssl_ca_name",
111 "Invalid CA certificate"},
112 {X509_V_ERR_PATH_LENGTH_EXCEEDED,
113 "X509_V_ERR_PATH_LENGTH_EXCEEDED",
114 "%err_name: %ssl_error_descr: %ssl_subject",
115 "Path length constraint exceeded"},
116 {X509_V_ERR_INVALID_PURPOSE,
117 "X509_V_ERR_INVALID_PURPOSE",
118 "%err_name: %ssl_error_descr: %ssl_subject",
119 "Unsupported certificate purpose"},
120 {X509_V_ERR_CERT_UNTRUSTED,
121 "X509_V_ERR_CERT_UNTRUSTED",
122 "%err_name: %ssl_error_descr: %ssl_subject",
123 "Certificate not trusted"},
124 {X509_V_ERR_CERT_REJECTED,
125 "X509_V_ERR_CERT_REJECTED",
126 "%err_name: %ssl_error_descr: %ssl_subject",
127 "Certificate rejected"},
128 {X509_V_ERR_SUBJECT_ISSUER_MISMATCH,
129 "X509_V_ERR_SUBJECT_ISSUER_MISMATCH",
130 "%err_name: %ssl_error_descr: %ssl_ca_name",
131 "Subject issuer mismatch"},
132 {X509_V_ERR_AKID_SKID_MISMATCH,
133 "X509_V_ERR_AKID_SKID_MISMATCH",
134 "%err_name: %ssl_error_descr: %ssl_subject",
135 "Authority and subject key identifier mismatch"},
136 {X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH,
137 "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH",
138 "%err_name: %ssl_error_descr: %ssl_ca_name",
139 "Authority and issuer serial number mismatch"},
140 {X509_V_ERR_KEYUSAGE_NO_CERTSIGN,
141 "X509_V_ERR_KEYUSAGE_NO_CERTSIGN",
142 "%err_name: %ssl_error_descr: %ssl_subject",
143 "Key usage does not include certificate signing"},
144 {X509_V_ERR_APPLICATION_VERIFICATION,
145 "X509_V_ERR_APPLICATION_VERIFICATION",
146 "%err_name: %ssl_error_descr: %ssl_subject",
147 "Application verification failure"},
148 { SSL_ERROR_NONE, "SSL_ERROR_NONE", "%err_name: No error", "No error" },
149 {SSL_ERROR_NONE, NULL, NULL, NULL }
150 };
151
152 static void loadSslDetailMap()
153 {
154 assert(TheSslDetail.empty());
155 for (int i = 0; TheSslDetailArray[i].name; ++i) {
156 TheSslDetail[TheSslDetailArray[i].value] = &TheSslDetailArray[i];
157 }
158 }
159
160 Ssl::ssl_error_t
161 Ssl::ParseErrorString(const char *name)
162 {
163 assert(name);
164
165 if (TheSslDetail.empty())
166 loadSslDetailMap();
167
168 typedef SslErrorDetails::const_iterator SEDCI;
169 for (SEDCI i = TheSslDetail.begin(); i != TheSslDetail.end(); ++i) {
170 if (strcmp(name, i->second->name) == 0)
171 return i->second->value;
172 }
173
174 if (xisdigit(*name)) {
175 const long int value = strtol(name, NULL, 0);
176 if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
177 return value;
178 fatalf("Too small or too bug SSL error code '%s'", name);
179 }
180
181 fatalf("Unknown SSL error name '%s'", name);
182 return SSL_ERROR_SSL; // not reached
183 }
184
185 static const SslErrorDetailEntry *getErrorRecord(Ssl::ssl_error_t value)
186 {
187 if (TheSslDetail.empty())
188 loadSslDetailMap();
189
190 const SslErrorDetails::const_iterator it = TheSslDetail.find(value);
191 if (it != TheSslDetail.end())
192 return it->second;
193
194 return NULL;
195 }
196
197 const char *
198 Ssl::GetErrorName(Ssl::ssl_error_t value)
199 {
200 if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
201 return errorRecord->name;
202
203 return NULL;
204 }
205
206 static const char *getErrorDetail(Ssl::ssl_error_t value)
207 {
208 if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
209 return errorRecord->detail;
210
211 // we must always return something because ErrorDetail::buildDetail
212 // will hit an assertion
213 return SslErrorDetailDefaultStr;
214 }
215
216 const char *
217 Ssl::GetErrorDescr(Ssl::ssl_error_t value)
218 {
219 if (const SslErrorDetailEntry *errorRecord = getErrorRecord(value))
220 return errorRecord->descr;
221
222 return NULL;
223 }
224
225 Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
226 {"ssl_subject", &Ssl::ErrorDetail::subject},
227 {"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
228 {"ssl_cn", &Ssl::ErrorDetail::cn},
229 {"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
230 {"ssl_notafter", &Ssl::ErrorDetail::notafter},
231 {"err_name", &Ssl::ErrorDetail::err_code},
232 {"ssl_error_descr", &Ssl::ErrorDetail::err_descr},
233 {NULL,NULL}
234 };
235
236 /**
237 * The subject of the current certification in text form
238 */
239 const char *Ssl::ErrorDetail::subject() const
240 {
241 if (!peer_cert)
242 return "[Not available]";
243
244 static char tmpBuffer[256]; // A temporary buffer
245 X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), tmpBuffer,
246 sizeof(tmpBuffer));
247 return tmpBuffer;
248 }
249
250 // helper function to be used with Ssl::matchX509CommonNames
251 static int copy_cn(void *check_data, ASN1_STRING *cn_data)
252 {
253 String *str = (String *)check_data;
254 if (!str) // no data? abort
255 return 0;
256 if (str->defined())
257 str->append(", ");
258 str->append((const char *)cn_data->data, cn_data->length);
259 return 1;
260 }
261
262 /**
263 * The list with certificates cn and alternate names
264 */
265 const char *Ssl::ErrorDetail::cn() const
266 {
267 if (!peer_cert)
268 return "[Not available]";
269
270 static String tmpStr; ///< A temporary string buffer
271 tmpStr.clean();
272 Ssl::matchX509CommonNames(peer_cert.get(), &tmpStr, copy_cn);
273 return tmpStr.termedBuf();
274 }
275
276 /**
277 * The issuer name
278 */
279 const char *Ssl::ErrorDetail::ca_name() const
280 {
281 if (!peer_cert)
282 return "[Not available]";
283
284 static char tmpBuffer[256]; // A temporary buffer
285 X509_NAME_oneline(X509_get_issuer_name(peer_cert.get()), tmpBuffer, sizeof(tmpBuffer));
286 return tmpBuffer;
287 }
288
289 /**
290 * The certificate "not before" field
291 */
292 const char *Ssl::ErrorDetail::notbefore() const
293 {
294 if (!peer_cert)
295 return "[Not available]";
296
297 static char tmpBuffer[256]; // A temporary buffer
298 ASN1_UTCTIME * tm = X509_get_notBefore(peer_cert.get());
299 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
300 return tmpBuffer;
301 }
302
303 /**
304 * The certificate "not after" field
305 */
306 const char *Ssl::ErrorDetail::notafter() const
307 {
308 if (!peer_cert)
309 return "[Not available]";
310
311 static char tmpBuffer[256]; // A temporary buffer
312 ASN1_UTCTIME * tm = X509_get_notAfter(peer_cert.get());
313 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
314 return tmpBuffer;
315 }
316
317 /**
318 * The string representation of the error_no
319 */
320 const char *Ssl::ErrorDetail::err_code() const
321 {
322 static char tmpBuffer[64];
323 const char *err = GetErrorName(error_no);
324 if (!err) {
325 snprintf(tmpBuffer, 64, "%d", (int)error_no);
326 err = tmpBuffer;
327 }
328 return err;
329 }
330
331 /**
332 * A short description of the error_no
333 */
334 const char *Ssl::ErrorDetail::err_descr() const
335 {
336 if (const char *err = GetErrorDescr(error_no))
337 return err;
338 return "[Not available]";
339 }
340
341 /**
342 * It converts the code to a string value. Currently the following
343 * formating codes are supported:
344 * %err_name: The name of the SSL error
345 * %ssl_error_descr: A short description of the SSL error
346 * %ssl_cn: The comma-separated list of common and alternate names
347 * %ssl_subject: The certificate subject
348 * %ssl_ca_name: The certificate issuer name
349 * %ssl_notbefore: The certificate "not before" field
350 * %ssl_notafter: The certificate "not after" field
351 \retval the length of the code (the number of characters will be replaced by value)
352 */
353 int Ssl::ErrorDetail::convert(const char *code, const char **value) const
354 {
355 *value = "-";
356 for (int i=0; ErrorFormatingCodes[i].code!=NULL; i++) {
357 const int len = strlen(ErrorFormatingCodes[i].code);
358 if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
359 ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
360 *value = (this->*action)();
361 return len;
362 }
363 }
364 return 0;
365 }
366
367 /**
368 * It uses the convert method to build the string errDetailStr using
369 * a template message for the current SSL error. The template messages
370 * can also contain normal error pages formating codes.
371 * Currently the error template messages are hard-coded
372 */
373 void Ssl::ErrorDetail::buildDetail() const
374 {
375 char const *s = getErrorDetail(error_no);
376 char const *p;
377 char const *t;
378 int code_len = 0;
379
380 assert(s);
381 while ((p = strchr(s, '%'))) {
382 errDetailStr.append(s, p - s);
383 code_len = convert(++p, &t);
384 if (code_len)
385 errDetailStr.append(t);
386 else
387 errDetailStr.append("%");
388 s = p + code_len;
389 }
390 errDetailStr.append(s, strlen(s));
391 }
392
393 const String &Ssl::ErrorDetail::toString() const
394 {
395 if (!errDetailStr.defined())
396 buildDetail();
397 return errDetailStr;
398 }
399
400 /* We may do not want to use X509_dup but instead
401 internal SSL locking:
402 CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
403 peer_cert.reset(cert);
404 */
405 Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert): error_no (err_no)
406 {
407 peer_cert.reset(X509_dup(cert));
408 }
409
410 Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
411 {
412 error_no = anErrDetail.error_no;
413 if (anErrDetail.peer_cert.get()) {
414 peer_cert.reset(X509_dup(anErrDetail.peer_cert.get()));
415 }
416 }