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