]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/helper.cc
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / ssl / helper.cc
1 /*
2 * Copyright (C) 1996-2020 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 "../helper.h"
11 #include "anyp/PortCfg.h"
12 #include "fs_io.h"
13 #include "helper/Reply.h"
14 #include "SquidConfig.h"
15 #include "SquidString.h"
16 #include "SquidTime.h"
17 #include "ssl/cert_validate_message.h"
18 #include "ssl/Config.h"
19 #include "ssl/helper.h"
20 #include "wordlist.h"
21
22 Ssl::CertValidationHelper::LruCache *Ssl::CertValidationHelper::HelperCache = nullptr;
23
24 #if USE_SSL_CRTD
25
26 namespace Ssl {
27
28 /// Initiator of an Ssl::Helper query.
29 class GeneratorRequestor {
30 public:
31 GeneratorRequestor(HLPCB *aCallback, void *aData): callback(aCallback), data(aData) {}
32 HLPCB *callback;
33 CallbackData data;
34 };
35
36 /// A pending Ssl::Helper request, combining the original and collapsed queries.
37 class GeneratorRequest {
38 CBDATA_CLASS(GeneratorRequest);
39
40 public:
41 /// adds a GeneratorRequestor
42 void emplace(HLPCB *callback, void *data) { requestors.emplace_back(callback, data); }
43
44 SBuf query; ///< Ssl::Helper request message (GeneratorRequests key)
45
46 /// Ssl::Helper request initiators waiting for the same answer (FIFO).
47 typedef std::vector<GeneratorRequestor> GeneratorRequestors;
48 GeneratorRequestors requestors;
49 };
50
51 /// Ssl::Helper query:GeneratorRequest map
52 typedef std::unordered_map<SBuf, GeneratorRequest*> GeneratorRequests;
53
54 static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply);
55
56 } // namespace Ssl
57
58 CBDATA_NAMESPACED_CLASS_INIT(Ssl, GeneratorRequest);
59
60 /// prints Ssl::GeneratorRequest for debugging
61 static std::ostream &
62 operator <<(std::ostream &os, const Ssl::GeneratorRequest &gr)
63 {
64 return os << "crtGenRq" << gr.query.id.value << "/" << gr.requestors.size();
65 }
66
67 /// pending Ssl::Helper requests (to all certificate generator helpers combined)
68 static Ssl::GeneratorRequests TheGeneratorRequests;
69
70 helper *Ssl::Helper::ssl_crtd = nullptr;
71
72 void Ssl::Helper::Init()
73 {
74 assert(ssl_crtd == NULL);
75
76 // we need to start ssl_crtd only if some port(s) need to bump SSL *and* generate certificates
77 // TODO: generate host certificates for SNI enabled accel ports
78 bool found = false;
79 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
80 found = s->flags.tunnelSslBumping && s->secure.generateHostCertificates;
81 if (!found)
82 return;
83
84 ssl_crtd = new helper(Ssl::TheConfig.ssl_crtd);
85 ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren);
86 ssl_crtd->ipc_type = IPC_STREAM;
87 // The crtd messages may contain the eol ('\n') character. We are
88 // going to use the '\1' char as the end-of-message mark.
89 ssl_crtd->eom = '\1';
90 assert(ssl_crtd->cmdline == NULL);
91 {
92 char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
93 char *tmp_begin = tmp;
94 char *token = NULL;
95 while ((token = strwordtok(NULL, &tmp))) {
96 wordlistAdd(&ssl_crtd->cmdline, token);
97 }
98 safe_free(tmp_begin);
99 }
100 helperOpenServers(ssl_crtd);
101 }
102
103 void Ssl::Helper::Shutdown()
104 {
105 if (!ssl_crtd)
106 return;
107 helperShutdown(ssl_crtd);
108 wordlistDestroy(&ssl_crtd->cmdline);
109 delete ssl_crtd;
110 ssl_crtd = NULL;
111 }
112
113 void
114 Ssl::Helper::Reconfigure()
115 {
116 Shutdown();
117 Init();
118 }
119
120 void Ssl::Helper::Submit(CrtdMessage const & message, HLPCB * callback, void * data)
121 {
122 SBuf rawMessage(message.compose().c_str()); // XXX: helpers cannot use SBuf
123 rawMessage.append("\n", 1);
124
125 const auto pending = TheGeneratorRequests.find(rawMessage);
126 if (pending != TheGeneratorRequests.end()) {
127 pending->second->emplace(callback, data);
128 debugs(83, 5, "collapsed request from " << data << " onto " << *pending->second);
129 return;
130 }
131
132 GeneratorRequest *request = new GeneratorRequest;
133 request->query = rawMessage;
134 request->emplace(callback, data);
135 TheGeneratorRequests.emplace(request->query, request);
136 debugs(83, 5, "request from " << data << " as " << *request);
137 // ssl_crtd becomes nil if Squid is reconfigured without SslBump or
138 // certificate generation disabled in the new configuration
139 if (ssl_crtd && ssl_crtd->trySubmit(request->query.c_str(), HandleGeneratorReply, request))
140 return;
141
142 ::Helper::Reply failReply(::Helper::BrokenHelper);
143 failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
144 HandleGeneratorReply(request, failReply);
145 }
146
147 /// receives helper response
148 static void
149 Ssl::HandleGeneratorReply(void *data, const ::Helper::Reply &reply)
150 {
151 const std::unique_ptr<Ssl::GeneratorRequest> request(static_cast<Ssl::GeneratorRequest*>(data));
152 assert(request);
153 const auto erased = TheGeneratorRequests.erase(request->query);
154 assert(erased);
155
156 for (auto &requestor: request->requestors) {
157 if (void *cbdata = requestor.data.validDone()) {
158 debugs(83, 5, "to " << cbdata << " in " << *request);
159 requestor.callback(cbdata, reply);
160 }
161 }
162 }
163 #endif //USE_SSL_CRTD
164
165 helper *Ssl::CertValidationHelper::ssl_crt_validator = nullptr;
166
167 void Ssl::CertValidationHelper::Init()
168 {
169 if (!Ssl::TheConfig.ssl_crt_validator)
170 return;
171
172 assert(ssl_crt_validator == NULL);
173
174 // we need to start ssl_crtd only if some port(s) need to bump SSL
175 bool found = false;
176 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
177 found = s->flags.tunnelSslBumping;
178 if (!found)
179 return;
180
181 ssl_crt_validator = new helper("ssl_crt_validator");
182 ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children);
183 ssl_crt_validator->ipc_type = IPC_STREAM;
184 // The crtd messages may contain the eol ('\n') character. We are
185 // going to use the '\1' char as the end-of-message mark.
186 ssl_crt_validator->eom = '\1';
187 assert(ssl_crt_validator->cmdline == NULL);
188
189 int ttl = 60;
190 size_t cache = 2048;
191 {
192 char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
193 char *tmp_begin = tmp;
194 char * token = NULL;
195 bool parseParams = true;
196 while ((token = strwordtok(NULL, &tmp))) {
197 if (parseParams) {
198 if (strncmp(token, "ttl=", 4) == 0) {
199 ttl = atoi(token + 4);
200 continue;
201 } else if (strncmp(token, "cache=", 6) == 0) {
202 cache = atoi(token + 6);
203 continue;
204 } else
205 parseParams = false;
206 }
207 wordlistAdd(&ssl_crt_validator->cmdline, token);
208 }
209 xfree(tmp_begin);
210 }
211 helperOpenServers(ssl_crt_validator);
212
213 //WARNING: initializing static member in an object initialization method
214 assert(HelperCache == NULL);
215 HelperCache = new Ssl::CertValidationHelper::LruCache(ttl, cache);
216 }
217
218 void Ssl::CertValidationHelper::Shutdown()
219 {
220 if (!ssl_crt_validator)
221 return;
222 helperShutdown(ssl_crt_validator);
223 wordlistDestroy(&ssl_crt_validator->cmdline);
224 delete ssl_crt_validator;
225 ssl_crt_validator = NULL;
226
227 // CertValidationHelper::HelperCache is a static member, it is not good policy to
228 // reset it here. Will work because the current Ssl::CertValidationHelper is
229 // always the same static object.
230 delete HelperCache;
231 HelperCache = NULL;
232 }
233
234 void
235 Ssl::CertValidationHelper::Reconfigure()
236 {
237 Shutdown();
238 Init();
239 }
240
241 class submitData
242 {
243 CBDATA_CLASS(submitData);
244
245 public:
246 SBuf query;
247 AsyncCall::Pointer callback;
248 Security::SessionPointer ssl;
249 };
250 CBDATA_CLASS_INIT(submitData);
251
252 static void
253 sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
254 {
255 Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
256 std::string error;
257
258 submitData *crtdvdData = static_cast<submitData *>(data);
259 assert(crtdvdData->ssl.get());
260 Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse(crtdvdData->ssl);
261 if (reply.result == ::Helper::BrokenHelper) {
262 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
263 validationResponse->resultCode = ::Helper::BrokenHelper;
264 } else if (!reply.other().hasContent()) {
265 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response");
266 validationResponse->resultCode = ::Helper::BrokenHelper;
267 } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
268 !replyMsg.parseResponse(*validationResponse, error) ) {
269 debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
270 debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
271 validationResponse->resultCode = ::Helper::BrokenHelper;
272 } else
273 validationResponse->resultCode = reply.result;
274
275 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(crtdvdData->callback->getDialer());
276 Must(dialer);
277 dialer->arg1 = validationResponse;
278 ScheduleCallHere(crtdvdData->callback);
279
280 if (Ssl::CertValidationHelper::HelperCache &&
281 (validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
282 Ssl::CertValidationResponse::Pointer *item = new Ssl::CertValidationResponse::Pointer(validationResponse);
283 if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, item))
284 delete item;
285 }
286
287 delete crtdvdData;
288 }
289
290 void Ssl::CertValidationHelper::Submit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
291 {
292 Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
293 message.setCode(Ssl::CertValidationMsg::code_cert_validate);
294 message.composeRequest(request);
295 debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
296
297 submitData *crtdvdData = new submitData;
298 crtdvdData->query.assign(message.compose().c_str());
299 crtdvdData->query.append('\n');
300 crtdvdData->callback = callback;
301 crtdvdData->ssl = request.ssl;
302 Ssl::CertValidationResponse::Pointer const*validationResponse;
303
304 if (CertValidationHelper::HelperCache &&
305 (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query))) {
306
307 CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
308 Must(dialer);
309 dialer->arg1 = *validationResponse;
310 ScheduleCallHere(callback);
311 delete crtdvdData;
312 return;
313 }
314
315 // ssl_crt_validator becomes nil if Squid is reconfigured with cert
316 // validator disabled in the new configuration
317 if (ssl_crt_validator && ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData))
318 return;
319
320 Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse(crtdvdData->ssl);
321 resp->resultCode = ::Helper::BrokenHelper;
322 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
323 Must(dialer);
324 dialer->arg1 = resp;
325 ScheduleCallHere(callback);
326 delete crtdvdData;
327 return;
328 }
329