]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/ErrorDetail.cc
Merge from trunk
[thirdparty/squid.git] / src / ssl / ErrorDetail.cc
1 #include "squid.h"
2 #include "ssl/ErrorDetail.h"
3
4 struct SslErrorDetailEntry {
5 Ssl::ssl_error_t value;
6 const char *name;
7 const char *detail;
8 };
9
10 // TODO: optimize by replacing with std::map or similar
11 static SslErrorDetailEntry TheSslDetailMap[] = {
12 { SQUID_X509_V_ERR_DOMAIN_MISMATCH,
13 "SQUID_X509_V_ERR_DOMAIN_MISMATCH",
14 "%err_name: The hostname you are connecting to (%H), does not match any of the Certificate valid names: %ssl_cn"},
15 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT,
16 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
17 "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" },
18 { X509_V_ERR_CERT_NOT_YET_VALID,
19 "X509_V_ERR_CERT_NOT_YET_VALID",
20 "%err_name: SSL Certficate is not valid before: %ssl_notbefore" },
21 { X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD,
22 "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
23 "%err_name: SSL Certificate has invalid start date (the 'not before' field): %subject" },
24 { X509_V_ERR_CERT_HAS_EXPIRED,
25 "X509_V_ERR_CERT_HAS_EXPIRED",
26 "%err_name: SSL Certificate expired on %ssl_notafter" },
27 { X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD,
28 "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
29 "%err_name: SSL Certificate has invalid expiration date (the 'not after' field): %ssl_subject" },
30 {X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT,
31 "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
32 "%err_name: Self-signed SSL Certificate: %ssl_subject"},
33 { X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY,
34 "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
35 "%err_name: SSL Certficate error: certificate issuer (CA) not known: %ssl_ca_name" },
36 { SSL_ERROR_NONE, "SSL_ERROR_NONE", "%err_name: No error" },
37 {SSL_ERROR_NONE, NULL, NULL }
38 };
39
40 Ssl::ssl_error_t
41 Ssl::parseErrorString(const char *name)
42 {
43 assert(name);
44
45 for (int i = 0; TheSslDetailMap[i].name; ++i) {
46 if (strcmp(name, TheSslDetailMap[i].name) == 0)
47 return TheSslDetailMap[i].value;
48 }
49
50 if (xisdigit(*name)) {
51 const long int value = strtol(name, NULL, 0);
52 if (SQUID_SSL_ERROR_MIN <= value && value <= SQUID_SSL_ERROR_MAX)
53 return value;
54 fatalf("Too small or too bug SSL error code '%s'", name);
55 }
56
57 fatalf("Unknown SSL error name '%s'", name);
58 return SSL_ERROR_SSL; // not reached
59 }
60
61 const char *
62 Ssl::getErrorName(Ssl::ssl_error_t value)
63 {
64
65 for (int i = 0; TheSslDetailMap[i].name; ++i) {
66 if (TheSslDetailMap[i].value == value)
67 return TheSslDetailMap[i].name;
68 }
69
70 return NULL;
71 }
72
73 static const char *getErrorDetail(Ssl::ssl_error_t value)
74 {
75 for (int i = 0; TheSslDetailMap[i].name; ++i) {
76 if (TheSslDetailMap[i].value == value)
77 return TheSslDetailMap[i].detail;
78 }
79
80 return NULL;
81 }
82
83 Ssl::ErrorDetail::err_frm_code Ssl::ErrorDetail::ErrorFormatingCodes[] = {
84 {"ssl_subject", &Ssl::ErrorDetail::subject},
85 {"ssl_ca_name", &Ssl::ErrorDetail::ca_name},
86 {"ssl_cn", &Ssl::ErrorDetail::cn},
87 {"ssl_notbefore", &Ssl::ErrorDetail::notbefore},
88 {"ssl_notafter", &Ssl::ErrorDetail::notafter},
89 {"err_name", &Ssl::ErrorDetail::err_code},
90 {NULL,NULL}
91 };
92
93 /**
94 * The subject of the current certification in text form
95 */
96 const char *Ssl::ErrorDetail::subject() const
97 {
98 if (!peer_cert)
99 return "[Not available]";
100
101 static char tmpBuffer[256]; // A temporary buffer
102 X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), tmpBuffer,
103 sizeof(tmpBuffer));
104 return tmpBuffer;
105 }
106
107 // helper function to be used with Ssl::matchX509CommonNames
108 static int copy_cn(void *check_data, ASN1_STRING *cn_data)
109 {
110 String *str = (String *)check_data;
111 if (!str) // no data? abort
112 return 0;
113 if (str->defined())
114 str->append(", ");
115 str->append((const char *)cn_data->data, cn_data->length);
116 return 1;
117 }
118
119 /**
120 * The list with certificates cn and alternate names
121 */
122 const char *Ssl::ErrorDetail::cn() const
123 {
124 if (!peer_cert)
125 return "[Not available]";
126
127 static String tmpStr; ///< A temporary string buffer
128 tmpStr.clean();
129 Ssl::matchX509CommonNames(peer_cert.get(), &tmpStr, copy_cn);
130 return tmpStr.termedBuf();
131 }
132
133 /**
134 * The issuer name
135 */
136 const char *Ssl::ErrorDetail::ca_name() const
137 {
138 if (!peer_cert)
139 return "[Not available]";
140
141 static char tmpBuffer[256]; // A temporary buffer
142 X509_NAME_oneline(X509_get_issuer_name(peer_cert.get()), tmpBuffer, sizeof(tmpBuffer));
143 return tmpBuffer;
144 }
145
146 /**
147 * The certificate "not before" field
148 */
149 const char *Ssl::ErrorDetail::notbefore() const
150 {
151 if (!peer_cert)
152 return "[Not available]";
153
154 static char tmpBuffer[256]; // A temporary buffer
155 ASN1_UTCTIME * tm = X509_get_notBefore(peer_cert.get());
156 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
157 return tmpBuffer;
158 }
159
160 /**
161 * The certificate "not after" field
162 */
163 const char *Ssl::ErrorDetail::notafter() const
164 {
165 if (!peer_cert)
166 return "[Not available]";
167
168 static char tmpBuffer[256]; // A temporary buffer
169 ASN1_UTCTIME * tm = X509_get_notAfter(peer_cert.get());
170 Ssl::asn1timeToString(tm, tmpBuffer, sizeof(tmpBuffer));
171 return tmpBuffer;
172 }
173
174 /**
175 * The string representation of the error_no
176 */
177 const char *Ssl::ErrorDetail::err_code() const
178 {
179 const char *err = getErrorName(error_no);
180 if (!err)
181 return "[Not available]";
182 return err;
183 }
184
185 /**
186 * It converts the code to a string value. Currently the following
187 * formating codes are supported:
188 * %err_name: The name of the SSL error
189 * %ssl_cn: The comma-separated list of common and alternate names
190 * %ssl_subject: The certificate subject
191 * %ssl_ca_name: The certificate issuer name
192 * %ssl_notbefore: The certificate "not before" field
193 * %ssl_notafter: The certificate "not after" field
194 \retval the length of the code (the number of characters will be replaced by value)
195 */
196 int Ssl::ErrorDetail::convert(const char *code, const char **value) const
197 {
198 *value = "-";
199 for (int i=0; ErrorFormatingCodes[i].code!=NULL; i++) {
200 const int len = strlen(ErrorFormatingCodes[i].code);
201 if (strncmp(code,ErrorFormatingCodes[i].code, len)==0) {
202 ErrorDetail::fmt_action_t action = ErrorFormatingCodes[i].fmt_action;
203 *value = (this->*action)();
204 return len;
205 }
206 }
207 return 0;
208 }
209
210 /**
211 * It uses the convert method to build the string errDetailStr using
212 * a template message for the current SSL error. The template messages
213 * can also contain normal error pages formating codes.
214 * Currently the error template messages are hard-coded
215 */
216 void Ssl::ErrorDetail::buildDetail() const
217 {
218 char const *s = getErrorDetail(error_no);
219 char const *p;
220 char const *t;
221 int code_len = 0;
222
223 if (!s) //May be add a default detail string?
224 return;
225
226 while ((p = strchr(s, '%'))) {
227 errDetailStr.append(s, p - s);
228 code_len = convert(++p, &t);
229 if (code_len)
230 errDetailStr.append(t);
231 else
232 errDetailStr.append("%");
233 s = p + code_len;
234 }
235 errDetailStr.append(s, strlen(s));
236 }
237
238 const String &Ssl::ErrorDetail::toString() const
239 {
240 if (!errDetailStr.defined())
241 buildDetail();
242 return errDetailStr;
243 }
244
245 /* We may do not want to use X509_dup but instead
246 internal SSL locking:
247 CRYPTO_add(&(cert->references),1,CRYPTO_LOCK_X509);
248 peer_cert.reset(cert);
249 */
250 Ssl::ErrorDetail::ErrorDetail( Ssl::ssl_error_t err_no, X509 *cert): error_no (err_no)
251 {
252 peer_cert.reset(X509_dup(cert));
253 }
254
255 Ssl::ErrorDetail::ErrorDetail(Ssl::ErrorDetail const &anErrDetail)
256 {
257 error_no = anErrDetail.error_no;
258 if (anErrDetail.peer_cert.get()) {
259 peer_cert.reset(X509_dup(anErrDetail.peer_cert.get()));
260 }
261 }