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 bool db_path_was_found
= false;
64 bool block_size_was_found
= false;
65 char buffer
[20] = "2048";
66 while ((token
= strwordtok(NULL
, &tmp
))) {
67 wordlistAdd(&ssl_crtd
->cmdline
, token
);
68 if (!strcmp(token
, "-b"))
69 block_size_was_found
= true;
70 if (!strcmp(token
, "-s")) {
71 db_path_was_found
= true;
72 } else if (db_path_was_found
) {
73 db_path_was_found
= false;
74 int fs_block_size
= 0;
75 fsBlockSize(token
, &fs_block_size
);
76 snprintf(buffer
, sizeof(buffer
), "%i", fs_block_size
);
79 if (!block_size_was_found
) {
80 wordlistAdd(&ssl_crtd
->cmdline
, "-b");
81 wordlistAdd(&ssl_crtd
->cmdline
, buffer
);
85 helperOpenServers(ssl_crtd
);
88 void Ssl::Helper::Shutdown()
92 helperShutdown(ssl_crtd
);
93 wordlistDestroy(&ssl_crtd
->cmdline
);
98 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
102 std::string msg
= message
.compose();
104 if (!ssl_crtd
->trySubmit(msg
.c_str(), callback
, data
)) {
105 ::Helper::Reply failReply
;
106 failReply
.result
= ::Helper::BrokenHelper
;
107 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
108 callback(data
, failReply
);
112 #endif //USE_SSL_CRTD
114 Ssl::CertValidationHelper
* Ssl::CertValidationHelper::GetInstance()
116 static Ssl::CertValidationHelper sslHelper
;
117 if (!Ssl::TheConfig
.ssl_crt_validator
)
122 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL
)
126 Ssl::CertValidationHelper::~CertValidationHelper()
131 void Ssl::CertValidationHelper::Init()
133 assert(ssl_crt_validator
== NULL
);
135 // we need to start ssl_crtd only if some port(s) need to bump SSL
137 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
138 found
= s
->flags
.tunnelSslBumping
;
142 ssl_crt_validator
= new helper("ssl_crt_validator");
143 ssl_crt_validator
->childs
.updateLimits(Ssl::TheConfig
.ssl_crt_validator_Children
);
144 ssl_crt_validator
->ipc_type
= IPC_STREAM
;
145 // The crtd messages may contain the eol ('\n') character. We are
146 // going to use the '\1' char as the end-of-message mark.
147 ssl_crt_validator
->eom
= '\1';
148 assert(ssl_crt_validator
->cmdline
== NULL
);
153 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
154 char *tmp_begin
= tmp
;
156 bool parseParams
= true;
157 while ((token
= strwordtok(NULL
, &tmp
))) {
159 if (strncmp(token
, "ttl=", 4) == 0) {
160 ttl
= atoi(token
+ 4);
162 } else if (strncmp(token
, "cache=", 6) == 0) {
163 cache
= atoi(token
+ 6);
168 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
172 helperOpenServers(ssl_crt_validator
);
174 //WARNING: initializing static member in an object initialization method
175 assert(HelperCache
== NULL
);
176 HelperCache
= new Ssl::CertValidationHelper::LruCache(ttl
, cache
);
179 void Ssl::CertValidationHelper::Shutdown()
181 if (!ssl_crt_validator
)
183 helperShutdown(ssl_crt_validator
);
184 wordlistDestroy(&ssl_crt_validator
->cmdline
);
185 delete ssl_crt_validator
;
186 ssl_crt_validator
= NULL
;
188 // CertValidationHelper::HelperCache is a static member, it is not good policy to
189 // reset it here. Will work because the current Ssl::CertValidationHelper is
190 // always the same static object.
197 CBDATA_CLASS(submitData
);
201 AsyncCall::Pointer callback
;
204 CBDATA_CLASS_INIT(submitData
);
207 sslCrtvdHandleReplyWrapper(void *data
, const ::Helper::Reply
&reply
)
209 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
210 Ssl::CertValidationResponse::Pointer validationResponse
= new Ssl::CertValidationResponse
;
213 submitData
*crtdvdData
= static_cast<submitData
*>(data
);
214 STACK_OF(X509
) *peerCerts
= SSL_get_peer_cert_chain(crtdvdData
->ssl
);
215 if (reply
.result
== ::Helper::BrokenHelper
) {
216 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
217 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
218 } else if (replyMsg
.parse(reply
.other().content(), reply
.other().contentSize()) != Ssl::CrtdMessage::OK
||
219 !replyMsg
.parseResponse(*validationResponse
, peerCerts
, error
) ) {
220 debugs(83, DBG_IMPORTANT
, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
221 debugs(83, DBG_IMPORTANT
, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg
.getBody());
222 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
224 validationResponse
->resultCode
= reply
.result
;
226 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(crtdvdData
->callback
->getDialer());
228 dialer
->arg1
= validationResponse
;
229 ScheduleCallHere(crtdvdData
->callback
);
231 if (Ssl::CertValidationHelper::HelperCache
&&
232 (validationResponse
->resultCode
== ::Helper::Okay
|| validationResponse
->resultCode
== ::Helper::Error
)) {
233 Ssl::CertValidationResponse::Pointer
*item
= new Ssl::CertValidationResponse::Pointer(validationResponse
);
234 Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
.c_str(), item
);
237 SSL_free(crtdvdData
->ssl
);
241 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, AsyncCall::Pointer
&callback
)
243 assert(ssl_crt_validator
);
245 Ssl::CertValidationMsg
message(Ssl::CrtdMessage::REQUEST
);
246 message
.setCode(Ssl::CertValidationMsg::code_cert_validate
);
247 message
.composeRequest(request
);
248 debugs(83, 5, "SSL crtvd request: " << message
.compose().c_str());
250 submitData
*crtdvdData
= new submitData
;
251 crtdvdData
->query
= message
.compose();
252 crtdvdData
->query
+= '\n';
253 crtdvdData
->callback
= callback
;
254 crtdvdData
->ssl
= request
.ssl
;
255 CRYPTO_add(&crtdvdData
->ssl
->references
,1,CRYPTO_LOCK_SSL
);
256 Ssl::CertValidationResponse::Pointer
const*validationResponse
;
258 if (CertValidationHelper::HelperCache
&&
259 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
.c_str()))) {
261 CertValidationHelper::CbDialer
*dialer
= dynamic_cast<CertValidationHelper::CbDialer
*>(callback
->getDialer());
263 dialer
->arg1
= *validationResponse
;
264 ScheduleCallHere(callback
);
265 SSL_free(crtdvdData
->ssl
);
270 if (!ssl_crt_validator
->trySubmit(crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
)) {
271 Ssl::CertValidationResponse::Pointer resp
= new Ssl::CertValidationResponse
;;
272 resp
->resultCode
= ::Helper::BrokenHelper
;
273 Ssl::CertValidationHelper::CbDialer
*dialer
= dynamic_cast<Ssl::CertValidationHelper::CbDialer
*>(callback
->getDialer());
276 ScheduleCallHere(callback
);
278 SSL_free(crtdvdData
->ssl
);