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