2 * Copyright (C) 1996-2017 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 Ssl::Helper
* Ssl::Helper::GetInstance()
72 static Ssl::Helper sslHelper
;
76 Ssl::Helper::Helper() : ssl_crtd(NULL
)
80 Ssl::Helper::~Helper()
85 void Ssl::Helper::Init()
87 assert(ssl_crtd
== NULL
);
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
92 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
93 found
= s
->flags
.tunnelSslBumping
&& s
->secure
.generateHostCertificates
;
97 ssl_crtd
= new helper(Ssl::TheConfig
.ssl_crtd
);
98 ssl_crtd
->childs
.updateLimits(Ssl::TheConfig
.ssl_crtdChildren
);
99 ssl_crtd
->ipc_type
= IPC_STREAM
;
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';
103 assert(ssl_crtd
->cmdline
== NULL
);
105 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
106 char *tmp_begin
= tmp
;
108 while ((token
= strwordtok(NULL
, &tmp
))) {
109 wordlistAdd(&ssl_crtd
->cmdline
, token
);
111 safe_free(tmp_begin
);
113 helperOpenServers(ssl_crtd
);
116 void Ssl::Helper::Shutdown()
120 helperShutdown(ssl_crtd
);
121 wordlistDestroy(&ssl_crtd
->cmdline
);
126 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
130 SBuf
rawMessage(message
.compose().c_str()); // XXX: helpers cannot use SBuf
131 rawMessage
.append("\n", 1);
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
);
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
))
148 ::Helper::Reply
failReply(::Helper::BrokenHelper
);
149 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
150 HandleGeneratorReply(request
, failReply
);
153 /// receives helper response
155 Ssl::HandleGeneratorReply(void *data
, const ::Helper::Reply
&reply
)
157 const std::unique_ptr
<Ssl::GeneratorRequest
> request(static_cast<Ssl::GeneratorRequest
*>(data
));
159 const auto erased
= TheGeneratorRequests
.erase(request
->query
);
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
);
169 #endif //USE_SSL_CRTD
171 Ssl::CertValidationHelper
* Ssl::CertValidationHelper::GetInstance()
173 static Ssl::CertValidationHelper sslHelper
;
174 if (!Ssl::TheConfig
.ssl_crt_validator
)
179 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL
)
183 Ssl::CertValidationHelper::~CertValidationHelper()
188 void Ssl::CertValidationHelper::Init()
190 assert(ssl_crt_validator
== NULL
);
192 // we need to start ssl_crtd only if some port(s) need to bump SSL
194 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
195 found
= s
->flags
.tunnelSslBumping
;
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
);
210 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
211 char *tmp_begin
= tmp
;
213 bool parseParams
= true;
214 while ((token
= strwordtok(NULL
, &tmp
))) {
216 if (strncmp(token
, "ttl=", 4) == 0) {
217 ttl
= atoi(token
+ 4);
219 } else if (strncmp(token
, "cache=", 6) == 0) {
220 cache
= atoi(token
+ 6);
225 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
229 helperOpenServers(ssl_crt_validator
);
231 //WARNING: initializing static member in an object initialization method
232 assert(HelperCache
== NULL
);
233 HelperCache
= new Ssl::CertValidationHelper::LruCache(ttl
, cache
);
236 void Ssl::CertValidationHelper::Shutdown()
238 if (!ssl_crt_validator
)
240 helperShutdown(ssl_crt_validator
);
241 wordlistDestroy(&ssl_crt_validator
->cmdline
);
242 delete ssl_crt_validator
;
243 ssl_crt_validator
= NULL
;
245 // CertValidationHelper::HelperCache is a static member, it is not good policy to
246 // reset it here. Will work because the current Ssl::CertValidationHelper is
247 // always the same static object.
254 CBDATA_CLASS(submitData
);
258 AsyncCall::Pointer callback
;
259 Security::SessionPointer ssl
;
261 CBDATA_CLASS_INIT(submitData
);
264 sslCrtvdHandleReplyWrapper(void *data
, const ::Helper::Reply
&reply
)
266 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
269 submitData
*crtdvdData
= static_cast<submitData
*>(data
);
270 assert(crtdvdData
->ssl
.get());
271 Ssl::CertValidationResponse::Pointer validationResponse
= new Ssl::CertValidationResponse(crtdvdData
->ssl
);
272 if (reply
.result
== ::Helper::BrokenHelper
) {
273 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
274 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
275 } else if (!reply
.other().hasContent()) {
276 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper returned NULL response");
277 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
278 } else if (replyMsg
.parse(reply
.other().content(), reply
.other().contentSize()) != Ssl::CrtdMessage::OK
||
279 !replyMsg
.parseResponse(*validationResponse
, error
) ) {
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());
282 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
284 validationResponse
->resultCode
= reply
.result
;
286 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(crtdvdData
->callback
->getDialer());
288 dialer
->arg1
= validationResponse
;
289 ScheduleCallHere(crtdvdData
->callback
);
291 if (Ssl::CertValidationHelper::HelperCache
&&
292 (validationResponse
->resultCode
== ::Helper::Okay
|| validationResponse
->resultCode
== ::Helper::Error
)) {
293 Ssl::CertValidationResponse::Pointer
*item
= new Ssl::CertValidationResponse::Pointer(validationResponse
);
294 if (!Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
, item
))
301 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, AsyncCall::Pointer
&callback
)
303 assert(ssl_crt_validator
);
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());
310 submitData
*crtdvdData
= new submitData
;
311 crtdvdData
->query
.assign(message
.compose().c_str());
312 crtdvdData
->query
.append('\n');
313 crtdvdData
->callback
= callback
;
314 crtdvdData
->ssl
= request
.ssl
;
315 Ssl::CertValidationResponse::Pointer
const*validationResponse
;
317 if (CertValidationHelper::HelperCache
&&
318 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
))) {
320 CertValidationHelper::CbDialer
*dialer
= dynamic_cast<CertValidationHelper::CbDialer
*>(callback
->getDialer());
322 dialer
->arg1
= *validationResponse
;
323 ScheduleCallHere(callback
);
328 if (!ssl_crt_validator
->trySubmit(crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
)) {
329 Ssl::CertValidationResponse::Pointer resp
= new Ssl::CertValidationResponse(crtdvdData
->ssl
);
330 resp
->resultCode
= ::Helper::BrokenHelper
;
331 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(callback
->getDialer());
334 ScheduleCallHere(callback
);