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