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