2 * Copyright (C) 1996-2016 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;
25 Ssl::Helper
* Ssl::Helper::GetInstance()
27 static Ssl::Helper sslHelper
;
31 Ssl::Helper::Helper() : ssl_crtd(NULL
)
35 Ssl::Helper::~Helper()
40 void Ssl::Helper::Init()
42 assert(ssl_crtd
== NULL
);
44 // we need to start ssl_crtd only if some port(s) need to bump SSL *and* generate certificates
45 // TODO: generate host certificates for SNI enabled accel ports
47 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
48 found
= s
->flags
.tunnelSslBumping
&& s
->generateHostCertificates
;
52 ssl_crtd
= new helper(Ssl::TheConfig
.ssl_crtd
);
53 ssl_crtd
->childs
.updateLimits(Ssl::TheConfig
.ssl_crtdChildren
);
54 ssl_crtd
->ipc_type
= IPC_STREAM
;
55 // The crtd messages may contain the eol ('\n') character. We are
56 // going to use the '\1' char as the end-of-message mark.
58 assert(ssl_crtd
->cmdline
== NULL
);
60 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
61 char *tmp_begin
= tmp
;
63 while ((token
= strwordtok(NULL
, &tmp
))) {
64 wordlistAdd(&ssl_crtd
->cmdline
, token
);
68 helperOpenServers(ssl_crtd
);
71 void Ssl::Helper::Shutdown()
75 helperShutdown(ssl_crtd
);
76 wordlistDestroy(&ssl_crtd
->cmdline
);
81 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
85 std::string msg
= message
.compose();
87 if (!ssl_crtd
->trySubmit(msg
.c_str(), callback
, data
)) {
88 ::Helper::Reply
failReply(::Helper::BrokenHelper
);
89 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
90 callback(data
, failReply
);
96 Ssl::CertValidationHelper
* Ssl::CertValidationHelper::GetInstance()
98 static Ssl::CertValidationHelper sslHelper
;
99 if (!Ssl::TheConfig
.ssl_crt_validator
)
104 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL
)
108 Ssl::CertValidationHelper::~CertValidationHelper()
113 void Ssl::CertValidationHelper::Init()
115 assert(ssl_crt_validator
== NULL
);
117 // we need to start ssl_crtd only if some port(s) need to bump SSL
119 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
120 found
= s
->flags
.tunnelSslBumping
;
124 ssl_crt_validator
= new helper("ssl_crt_validator");
125 ssl_crt_validator
->childs
.updateLimits(Ssl::TheConfig
.ssl_crt_validator_Children
);
126 ssl_crt_validator
->ipc_type
= IPC_STREAM
;
127 // The crtd messages may contain the eol ('\n') character. We are
128 // going to use the '\1' char as the end-of-message mark.
129 ssl_crt_validator
->eom
= '\1';
130 assert(ssl_crt_validator
->cmdline
== NULL
);
135 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
136 char *tmp_begin
= tmp
;
138 bool parseParams
= true;
139 while ((token
= strwordtok(NULL
, &tmp
))) {
141 if (strncmp(token
, "ttl=", 4) == 0) {
142 ttl
= atoi(token
+ 4);
144 } else if (strncmp(token
, "cache=", 6) == 0) {
145 cache
= atoi(token
+ 6);
150 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
154 helperOpenServers(ssl_crt_validator
);
156 //WARNING: initializing static member in an object initialization method
157 assert(HelperCache
== NULL
);
158 HelperCache
= new Ssl::CertValidationHelper::LruCache(ttl
, cache
);
161 void Ssl::CertValidationHelper::Shutdown()
163 if (!ssl_crt_validator
)
165 helperShutdown(ssl_crt_validator
);
166 wordlistDestroy(&ssl_crt_validator
->cmdline
);
167 delete ssl_crt_validator
;
168 ssl_crt_validator
= NULL
;
170 // CertValidationHelper::HelperCache is a static member, it is not good policy to
171 // reset it here. Will work because the current Ssl::CertValidationHelper is
172 // always the same static object.
179 CBDATA_CLASS(submitData
);
183 AsyncCall::Pointer callback
;
184 Security::SessionPointer ssl
;
186 CBDATA_CLASS_INIT(submitData
);
189 sslCrtvdHandleReplyWrapper(void *data
, const ::Helper::Reply
&reply
)
191 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
192 Ssl::CertValidationResponse::Pointer validationResponse
= new Ssl::CertValidationResponse
;
195 submitData
*crtdvdData
= static_cast<submitData
*>(data
);
196 STACK_OF(X509
) *peerCerts
= SSL_get_peer_cert_chain(crtdvdData
->ssl
.get());
197 if (reply
.result
== ::Helper::BrokenHelper
) {
198 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
199 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
200 } else if (!reply
.other().hasContent()) {
201 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper returned NULL response");
202 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
203 } else if (replyMsg
.parse(reply
.other().content(), reply
.other().contentSize()) != Ssl::CrtdMessage::OK
||
204 !replyMsg
.parseResponse(*validationResponse
, peerCerts
, error
) ) {
205 debugs(83, DBG_IMPORTANT
, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
206 debugs(83, DBG_IMPORTANT
, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg
.getBody());
207 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
209 validationResponse
->resultCode
= reply
.result
;
211 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(crtdvdData
->callback
->getDialer());
213 dialer
->arg1
= validationResponse
;
214 ScheduleCallHere(crtdvdData
->callback
);
216 if (Ssl::CertValidationHelper::HelperCache
&&
217 (validationResponse
->resultCode
== ::Helper::Okay
|| validationResponse
->resultCode
== ::Helper::Error
)) {
218 Ssl::CertValidationResponse::Pointer
*item
= new Ssl::CertValidationResponse::Pointer(validationResponse
);
219 if (!Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
.c_str(), item
))
226 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, AsyncCall::Pointer
&callback
)
228 assert(ssl_crt_validator
);
230 Ssl::CertValidationMsg
message(Ssl::CrtdMessage::REQUEST
);
231 message
.setCode(Ssl::CertValidationMsg::code_cert_validate
);
232 message
.composeRequest(request
);
233 debugs(83, 5, "SSL crtvd request: " << message
.compose().c_str());
235 submitData
*crtdvdData
= new submitData
;
236 crtdvdData
->query
= message
.compose();
237 crtdvdData
->query
+= '\n';
238 crtdvdData
->callback
= callback
;
239 crtdvdData
->ssl
= request
.ssl
;
240 Ssl::CertValidationResponse::Pointer
const*validationResponse
;
242 if (CertValidationHelper::HelperCache
&&
243 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
.c_str()))) {
245 CertValidationHelper::CbDialer
*dialer
= dynamic_cast<CertValidationHelper::CbDialer
*>(callback
->getDialer());
247 dialer
->arg1
= *validationResponse
;
248 ScheduleCallHere(callback
);
253 if (!ssl_crt_validator
->trySubmit(crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
)) {
254 Ssl::CertValidationResponse::Pointer resp
= new Ssl::CertValidationResponse
;;
255 resp
->resultCode
= ::Helper::BrokenHelper
;
256 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(callback
->getDialer());
259 ScheduleCallHere(callback
);