2 * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
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.
10 #include "../helper.h"
11 #include "anyp/PortCfg.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"
22 Ssl::CertValidationHelper::LruCache
*Ssl::CertValidationHelper::HelperCache
= nullptr;
28 /// Initiator of an Ssl::Helper query.
29 class GeneratorRequestor
{
31 GeneratorRequestor(HLPCB
*aCallback
, void *aData
): callback(aCallback
), data(aData
) {}
36 /// A pending Ssl::Helper request, combining the original and collapsed queries.
37 class GeneratorRequest
{
38 CBDATA_CLASS(GeneratorRequest
);
41 /// adds a GeneratorRequestor
42 void emplace(HLPCB
*callback
, void *data
) { requestors
.emplace_back(callback
, data
); }
44 SBuf query
; ///< Ssl::Helper request message (GeneratorRequests key)
46 /// Ssl::Helper request initiators waiting for the same answer (FIFO).
47 typedef std::vector
<GeneratorRequestor
> GeneratorRequestors
;
48 GeneratorRequestors requestors
;
51 /// Ssl::Helper query:GeneratorRequest map
52 typedef std::unordered_map
<SBuf
, GeneratorRequest
*> GeneratorRequests
;
54 static void HandleGeneratorReply(void *data
, const ::Helper::Reply
&reply
);
58 CBDATA_NAMESPACED_CLASS_INIT(Ssl
, GeneratorRequest
);
60 /// prints Ssl::GeneratorRequest for debugging
62 operator <<(std::ostream
&os
, const Ssl::GeneratorRequest
&gr
)
64 return os
<< "crtGenRq" << gr
.query
.id
.value
<< "/" << gr
.requestors
.size();
67 /// pending Ssl::Helper requests (to all certificate generator helpers combined)
68 static Ssl::GeneratorRequests TheGeneratorRequests
;
70 helper
*Ssl::Helper::ssl_crtd
= nullptr;
72 void Ssl::Helper::Init()
74 assert(ssl_crtd
== NULL
);
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
79 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
80 found
= s
->flags
.tunnelSslBumping
&& s
->secure
.generateHostCertificates
;
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.
90 assert(ssl_crtd
->cmdline
== NULL
);
92 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
93 char *tmp_begin
= tmp
;
95 while ((token
= strwordtok(NULL
, &tmp
))) {
96 wordlistAdd(&ssl_crtd
->cmdline
, token
);
100 helperOpenServers(ssl_crtd
);
103 void Ssl::Helper::Shutdown()
107 helperShutdown(ssl_crtd
);
108 wordlistDestroy(&ssl_crtd
->cmdline
);
114 Ssl::Helper::Reconfigure()
120 void Ssl::Helper::Submit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
122 SBuf
rawMessage(message
.compose().c_str()); // XXX: helpers cannot use SBuf
123 rawMessage
.append("\n", 1);
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
);
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
))
142 ::Helper::Reply
failReply(::Helper::BrokenHelper
);
143 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
144 HandleGeneratorReply(request
, failReply
);
147 /// receives helper response
149 Ssl::HandleGeneratorReply(void *data
, const ::Helper::Reply
&reply
)
151 const std::unique_ptr
<Ssl::GeneratorRequest
> request(static_cast<Ssl::GeneratorRequest
*>(data
));
153 const auto erased
= TheGeneratorRequests
.erase(request
->query
);
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
);
163 #endif //USE_SSL_CRTD
165 helper
*Ssl::CertValidationHelper::ssl_crt_validator
= nullptr;
167 void Ssl::CertValidationHelper::Init()
169 if (!Ssl::TheConfig
.ssl_crt_validator
)
172 assert(ssl_crt_validator
== NULL
);
174 // we need to start ssl_crtd only if some port(s) need to bump SSL
176 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
177 found
= s
->flags
.tunnelSslBumping
;
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
);
192 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
193 char *tmp_begin
= tmp
;
195 bool parseParams
= true;
196 while ((token
= strwordtok(NULL
, &tmp
))) {
198 if (strncmp(token
, "ttl=", 4) == 0) {
199 ttl
= atoi(token
+ 4);
201 } else if (strncmp(token
, "cache=", 6) == 0) {
202 cache
= atoi(token
+ 6);
207 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
211 helperOpenServers(ssl_crt_validator
);
213 //WARNING: initializing static member in an object initialization method
214 assert(HelperCache
== NULL
);
215 HelperCache
= new Ssl::CertValidationHelper::LruCache(ttl
, cache
);
218 void Ssl::CertValidationHelper::Shutdown()
220 if (!ssl_crt_validator
)
222 helperShutdown(ssl_crt_validator
);
223 wordlistDestroy(&ssl_crt_validator
->cmdline
);
224 delete ssl_crt_validator
;
225 ssl_crt_validator
= NULL
;
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.
235 Ssl::CertValidationHelper::Reconfigure()
243 CBDATA_CLASS(submitData
);
247 AsyncCall::Pointer callback
;
248 Security::SessionPointer ssl
;
250 CBDATA_CLASS_INIT(submitData
);
253 sslCrtvdHandleReplyWrapper(void *data
, const ::Helper::Reply
&reply
)
255 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
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
;
273 validationResponse
->resultCode
= reply
.result
;
275 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(crtdvdData
->callback
->getDialer());
277 dialer
->arg1
= validationResponse
;
278 ScheduleCallHere(crtdvdData
->callback
);
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
))
290 void Ssl::CertValidationHelper::Submit(Ssl::CertValidationRequest
const &request
, AsyncCall::Pointer
&callback
)
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());
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
;
304 if (CertValidationHelper::HelperCache
&&
305 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
))) {
307 CertValidationHelper::CbDialer
*dialer
= dynamic_cast<CertValidationHelper::CbDialer
*>(callback
->getDialer());
309 dialer
->arg1
= *validationResponse
;
310 ScheduleCallHere(callback
);
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
))
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());
325 ScheduleCallHere(callback
);