]>
Commit | Line | Data |
---|---|---|
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 | 8 | struct SslErrorEntry { |
461b9576 | 9 | Ssl::ssl_error_t value; |
4d16918e | 10 | const char *name; |
4d16918e CT |
11 | }; |
12 | ||
8e9bae99 | 13 | static const char *SslErrorDetailDefaultStr = "SSL handshake error (%err_name)"; |
cf09bec7 | 14 | //Use std::map to optimize search |
02259ff8 CT |
15 | typedef std::map<Ssl::ssl_error_t, const SslErrorEntry *> SslErrors; |
16 | SslErrors TheSslErrors; | |
cf09bec7 | 17 | |
02259ff8 | 18 | static 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 | 91 | static 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 |
99 | Ssl::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 | 108 | Ssl::ssl_error_t |
5e430bf3 | 109 | Ssl::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 | 128 | const 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 |
140 | const char * |
141 | Ssl::GetErrorDescr(Ssl::ssl_error_t value) | |
142 | { | |
02259ff8 | 143 | return ErrorDetailsManager::GetInstance().getDefaultErrorDescr(value); |
cf09bec7 CT |
144 | } |
145 | ||
e34763f4 | 146 | Ssl::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 | */ | |
161 | const 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 | |
173 | static 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 | */ | |
187 | const 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 | */ | |
201 | const 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 | */ | |
214 | const 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 | */ | |
228 | const 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 | */ | |
242 | const 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 | */ | |
264 | const 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 |
273 | const 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 | */ | |
294 | int 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 | */ | |
314 | void 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 |
340 | const 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 | 352 | Ssl::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 | ||
360 | Ssl::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 | } |