]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/helper.cc
Merged from trunk
[thirdparty/squid.git] / src / ssl / helper.cc
1 #include "squid.h"
2 #include "anyp/PortCfg.h"
3 #include "ssl/Config.h"
4 #include "ssl/helper.h"
5 #include "SquidString.h"
6 #include "SquidTime.h"
7 #include "SwapDir.h"
8 #include "ssl/cert_validate_message.h"
9 #include "wordlist.h"
10 #include "SquidConfig.h"
11
12 LruMap<Ssl::CertValidationResponse> *Ssl::CertValidationHelper::HelperCache = NULL;
13
14 #if USE_SSL_CRTD
15 Ssl::Helper * Ssl::Helper::GetInstance()
16 {
17 static Ssl::Helper sslHelper;
18 return &sslHelper;
19 }
20
21 Ssl::Helper::Helper() : ssl_crtd(NULL)
22 {
23 }
24
25 Ssl::Helper::~Helper()
26 {
27 Shutdown();
28 }
29
30 void Ssl::Helper::Init()
31 {
32 assert(ssl_crtd == NULL);
33
34 // we need to start ssl_crtd only if some port(s) need to bump SSL
35 bool found = false;
36 for (AnyP::PortCfg *s = ::Config.Sockaddr.http; !found && s; s = s->next)
37 found = s->flags.tunnelSslBumping;
38 for (AnyP::PortCfg *s = ::Config.Sockaddr.https; !found && s; s = s->next)
39 found = s->flags.tunnelSslBumping;
40 if (!found)
41 return;
42
43 ssl_crtd = new helper("ssl_crtd");
44 ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren);
45 ssl_crtd->ipc_type = IPC_STREAM;
46 // The crtd messages may contain the eol ('\n') character. We are
47 // going to use the '\1' char as the end-of-message mark.
48 ssl_crtd->eom = '\1';
49 assert(ssl_crtd->cmdline == NULL);
50 {
51 char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
52 char *tmp_begin = tmp;
53 char * token = NULL;
54 bool db_path_was_found = false;
55 bool block_size_was_found = false;
56 char buffer[20] = "2048";
57 while ((token = strwordtok(NULL, &tmp))) {
58 wordlistAdd(&ssl_crtd->cmdline, token);
59 if (!strcmp(token, "-b"))
60 block_size_was_found = true;
61 if (!strcmp(token, "-s")) {
62 db_path_was_found = true;
63 } else if (db_path_was_found) {
64 db_path_was_found = false;
65 int fs_block_size = 0;
66 storeDirGetBlkSize(token, &fs_block_size);
67 snprintf(buffer, sizeof(buffer), "%i", fs_block_size);
68 }
69 }
70 if (!block_size_was_found) {
71 wordlistAdd(&ssl_crtd->cmdline, "-b");
72 wordlistAdd(&ssl_crtd->cmdline, buffer);
73 }
74 safe_free(tmp_begin);
75 }
76 helperOpenServers(ssl_crtd);
77 }
78
79 void Ssl::Helper::Shutdown()
80 {
81 if (!ssl_crtd)
82 return;
83 helperShutdown(ssl_crtd);
84 wordlistDestroy(&ssl_crtd->cmdline);
85 delete ssl_crtd;
86 ssl_crtd = NULL;
87 }
88
89 void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
90 {
91 static time_t first_warn = 0;
92 assert(ssl_crtd);
93
94 if (ssl_crtd->stats.queue_size >= (int)(ssl_crtd->childs.n_running * 2)) {
95 if (first_warn == 0)
96 first_warn = squid_curtime;
97 if (squid_curtime - first_warn > 3 * 60)
98 fatal("SSL servers not responding for 3 minutes");
99 debugs(34, DBG_IMPORTANT, HERE << "Queue overload, rejecting");
100 HelperReply failReply;
101 failReply.result = HelperReply::BrokenHelper;
102 failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
103 callback(data, failReply);
104 return;
105 }
106
107 first_warn = 0;
108 std::string msg = message.compose();
109 msg += '\n';
110 helperSubmit(ssl_crtd, msg.c_str(), callback, data);
111 }
112 #endif //USE_SSL_CRTD
113
114 Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
115 {
116 static Ssl::CertValidationHelper sslHelper;
117 if (!Ssl::TheConfig.ssl_crt_validator)
118 return NULL;
119 return &sslHelper;
120 }
121
122 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL)
123 {
124 }
125
126 Ssl::CertValidationHelper::~CertValidationHelper()
127 {
128 Shutdown();
129 }
130
131 void Ssl::CertValidationHelper::Init()
132 {
133 assert(ssl_crt_validator == NULL);
134
135 // we need to start ssl_crtd only if some port(s) need to bump SSL
136 bool found = false;
137 for (AnyP::PortCfg *s = ::Config.Sockaddr.http; !found && s; s = s->next)
138 found = s->flags.tunnelSslBumping;
139 for (AnyP::PortCfg *s = ::Config.Sockaddr.https; !found && s; s = s->next)
140 found = s->flags.tunnelSslBumping;
141 if (!found)
142 return;
143
144 ssl_crt_validator = new helper("ssl_crt_validator");
145 ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children);
146 ssl_crt_validator->ipc_type = IPC_STREAM;
147 // The crtd messages may contain the eol ('\n') character. We are
148 // going to use the '\1' char as the end-of-message mark.
149 ssl_crt_validator->eom = '\1';
150 assert(ssl_crt_validator->cmdline == NULL);
151
152 int ttl = 60;
153 size_t cache = 2048;
154 {
155 char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
156 char *tmp_begin = tmp;
157 char * token = NULL;
158 bool parseParams = true;
159 while ((token = strwordtok(NULL, &tmp))) {
160 if (parseParams) {
161 if (strncmp(token, "ttl=", 4) == 0) {
162 ttl = atoi(token + 4);
163 continue;
164 } else if (strncmp(token, "cache=", 6) == 0) {
165 cache = atoi(token + 6);
166 continue;
167 } else
168 parseParams = false;
169 }
170 wordlistAdd(&ssl_crt_validator->cmdline, token);
171 }
172 xfree(tmp_begin);
173 }
174 helperOpenServers(ssl_crt_validator);
175
176 //WARNING: initializing static member in an object initialization method
177 assert(HelperCache == NULL);
178 HelperCache = new LruMap<Ssl::CertValidationResponse>(ttl, cache);
179 }
180
181 void Ssl::CertValidationHelper::Shutdown()
182 {
183 if (!ssl_crt_validator)
184 return;
185 helperShutdown(ssl_crt_validator);
186 wordlistDestroy(&ssl_crt_validator->cmdline);
187 delete ssl_crt_validator;
188 ssl_crt_validator = NULL;
189
190 // CertValidationHelper::HelperCache is a static member, it is not good policy to
191 // reset it here. Will work because the current Ssl::CertValidationHelper is
192 // always the same static object.
193 delete HelperCache;
194 HelperCache = NULL;
195 }
196
197 struct submitData {
198 std::string query;
199 Ssl::CertValidationHelper::CVHCB *callback;
200 void *data;
201 SSL *ssl;
202 CBDATA_CLASS2(submitData);
203 };
204 CBDATA_CLASS_INIT(submitData);
205
206 static void
207 sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply)
208 {
209 Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
210 Ssl::CertValidationResponse *validationResponse = new Ssl::CertValidationResponse;
211 std::string error;
212
213 submitData *crtdvdData = static_cast<submitData *>(data);
214 STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl);
215 if (reply.result == HelperReply::BrokenHelper) {
216 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
217 validationResponse->resultCode = HelperReply::BrokenHelper;
218 } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
219 !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) {
220 debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
221 debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
222 validationResponse->resultCode = HelperReply::BrokenHelper;
223 } else
224 validationResponse->resultCode = reply.result;
225
226 crtdvdData->callback(crtdvdData->data, *validationResponse);
227
228 if (Ssl::CertValidationHelper::HelperCache &&
229 (validationResponse->resultCode == HelperReply::Okay || validationResponse->resultCode == HelperReply::Error)) {
230 Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), validationResponse);
231 } else
232 delete validationResponse;
233
234 cbdataReferenceDone(crtdvdData->data);
235 SSL_free(crtdvdData->ssl);
236 delete crtdvdData;
237 }
238
239 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, Ssl::CertValidationHelper::CVHCB * callback, void * data)
240 {
241 static time_t first_warn = 0;
242 assert(ssl_crt_validator);
243
244 if (ssl_crt_validator->stats.queue_size >= (int)(ssl_crt_validator->childs.n_running * 2)) {
245 if (first_warn == 0)
246 first_warn = squid_curtime;
247 if (squid_curtime - first_warn > 3 * 60)
248 fatal("ssl_crtvd queue being overloaded for long time");
249 debugs(83, DBG_IMPORTANT, "WARNING: ssl_crtvd queue overload, rejecting");
250 Ssl::CertValidationResponse resp;
251 resp.resultCode = HelperReply::BrokenHelper;
252 callback(data, resp);
253 return;
254 }
255 first_warn = 0;
256
257 Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
258 message.setCode(Ssl::CertValidationMsg::code_cert_validate);
259 message.composeRequest(request);
260 debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
261
262 submitData *crtdvdData = new submitData;
263 crtdvdData->query = message.compose();
264 crtdvdData->query += '\n';
265 crtdvdData->callback = callback;
266 crtdvdData->data = cbdataReference(data);
267 crtdvdData->ssl = request.ssl;
268 CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL);
269 Ssl::CertValidationResponse const*validationResponse;
270
271 if (CertValidationHelper::HelperCache &&
272 (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
273 callback(data, *validationResponse);
274 cbdataReferenceDone(crtdvdData->data);
275 SSL_free(crtdvdData->ssl);
276 delete crtdvdData;
277 return;
278 }
279 helperSubmit(ssl_crt_validator, crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData);
280 }