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