2 * Copyright (C) 1996-2014 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 "anyp/PortCfg.h"
11 #include "SquidConfig.h"
12 #include "SquidString.h"
13 #include "SquidTime.h"
14 #include "ssl/cert_validate_message.h"
15 #include "ssl/Config.h"
16 #include "ssl/helper.h"
20 LruMap
<Ssl::CertValidationResponse
> *Ssl::CertValidationHelper::HelperCache
= NULL
;
23 Ssl::Helper
* Ssl::Helper::GetInstance()
25 static Ssl::Helper sslHelper
;
29 Ssl::Helper::Helper() : ssl_crtd(NULL
)
33 Ssl::Helper::~Helper()
38 void Ssl::Helper::Init()
40 assert(ssl_crtd
== NULL
);
42 // we need to start ssl_crtd only if some port(s) need to bump SSL
44 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
45 found
= s
->flags
.tunnelSslBumping
;
46 for (AnyP::PortCfgPointer s
= HttpsPortList
; !found
&& s
!= NULL
; s
= s
->next
)
47 found
= s
->flags
.tunnelSslBumping
;
51 ssl_crtd
= new helper("ssl_crtd");
52 ssl_crtd
->childs
.updateLimits(Ssl::TheConfig
.ssl_crtdChildren
);
53 ssl_crtd
->ipc_type
= IPC_STREAM
;
54 // The crtd messages may contain the eol ('\n') character. We are
55 // going to use the '\1' char as the end-of-message mark.
57 assert(ssl_crtd
->cmdline
== NULL
);
59 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
60 char *tmp_begin
= tmp
;
62 bool db_path_was_found
= false;
63 bool block_size_was_found
= false;
64 char buffer
[20] = "2048";
65 while ((token
= strwordtok(NULL
, &tmp
))) {
66 wordlistAdd(&ssl_crtd
->cmdline
, token
);
67 if (!strcmp(token
, "-b"))
68 block_size_was_found
= true;
69 if (!strcmp(token
, "-s")) {
70 db_path_was_found
= true;
71 } else if (db_path_was_found
) {
72 db_path_was_found
= false;
73 int fs_block_size
= 0;
74 storeDirGetBlkSize(token
, &fs_block_size
);
75 snprintf(buffer
, sizeof(buffer
), "%i", fs_block_size
);
78 if (!block_size_was_found
) {
79 wordlistAdd(&ssl_crtd
->cmdline
, "-b");
80 wordlistAdd(&ssl_crtd
->cmdline
, buffer
);
84 helperOpenServers(ssl_crtd
);
87 void Ssl::Helper::Shutdown()
91 helperShutdown(ssl_crtd
);
92 wordlistDestroy(&ssl_crtd
->cmdline
);
97 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
99 static time_t first_warn
= 0;
102 if (ssl_crtd
->stats
.queue_size
>= (int)(ssl_crtd
->childs
.n_running
* 2)) {
104 first_warn
= squid_curtime
;
105 if (squid_curtime
- first_warn
> 3 * 60)
106 fatal("SSL servers not responding for 3 minutes");
107 debugs(34, DBG_IMPORTANT
, HERE
<< "Queue overload, rejecting");
108 HelperReply failReply
;
109 failReply
.result
= HelperReply::BrokenHelper
;
110 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
111 callback(data
, failReply
);
116 std::string msg
= message
.compose();
118 helperSubmit(ssl_crtd
, msg
.c_str(), callback
, data
);
120 #endif //USE_SSL_CRTD
122 Ssl::CertValidationHelper
* Ssl::CertValidationHelper::GetInstance()
124 static Ssl::CertValidationHelper sslHelper
;
125 if (!Ssl::TheConfig
.ssl_crt_validator
)
130 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL
)
134 Ssl::CertValidationHelper::~CertValidationHelper()
139 void Ssl::CertValidationHelper::Init()
141 assert(ssl_crt_validator
== NULL
);
143 // we need to start ssl_crtd only if some port(s) need to bump SSL
145 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
146 found
= s
->flags
.tunnelSslBumping
;
147 for (AnyP::PortCfgPointer s
= HttpsPortList
; !found
&& s
!= NULL
; s
= s
->next
)
148 found
= s
->flags
.tunnelSslBumping
;
152 ssl_crt_validator
= new helper("ssl_crt_validator");
153 ssl_crt_validator
->childs
.updateLimits(Ssl::TheConfig
.ssl_crt_validator_Children
);
154 ssl_crt_validator
->ipc_type
= IPC_STREAM
;
155 // The crtd messages may contain the eol ('\n') character. We are
156 // going to use the '\1' char as the end-of-message mark.
157 ssl_crt_validator
->eom
= '\1';
158 assert(ssl_crt_validator
->cmdline
== NULL
);
163 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
164 char *tmp_begin
= tmp
;
166 bool parseParams
= true;
167 while ((token
= strwordtok(NULL
, &tmp
))) {
169 if (strncmp(token
, "ttl=", 4) == 0) {
170 ttl
= atoi(token
+ 4);
172 } else if (strncmp(token
, "cache=", 6) == 0) {
173 cache
= atoi(token
+ 6);
178 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
182 helperOpenServers(ssl_crt_validator
);
184 //WARNING: initializing static member in an object initialization method
185 assert(HelperCache
== NULL
);
186 HelperCache
= new LruMap
<Ssl::CertValidationResponse
>(ttl
, cache
);
189 void Ssl::CertValidationHelper::Shutdown()
191 if (!ssl_crt_validator
)
193 helperShutdown(ssl_crt_validator
);
194 wordlistDestroy(&ssl_crt_validator
->cmdline
);
195 delete ssl_crt_validator
;
196 ssl_crt_validator
= NULL
;
198 // CertValidationHelper::HelperCache is a static member, it is not good policy to
199 // reset it here. Will work because the current Ssl::CertValidationHelper is
200 // always the same static object.
207 Ssl::CertValidationHelper::CVHCB
*callback
;
210 CBDATA_CLASS2(submitData
);
212 CBDATA_CLASS_INIT(submitData
);
215 sslCrtvdHandleReplyWrapper(void *data
, const HelperReply
&reply
)
217 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
218 Ssl::CertValidationResponse
*validationResponse
= new Ssl::CertValidationResponse
;
221 submitData
*crtdvdData
= static_cast<submitData
*>(data
);
222 STACK_OF(X509
) *peerCerts
= SSL_get_peer_cert_chain(crtdvdData
->ssl
);
223 if (reply
.result
== HelperReply::BrokenHelper
) {
224 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
225 validationResponse
->resultCode
= HelperReply::BrokenHelper
;
226 } else if (replyMsg
.parse(reply
.other().content(), reply
.other().contentSize()) != Ssl::CrtdMessage::OK
||
227 !replyMsg
.parseResponse(*validationResponse
, peerCerts
, error
) ) {
228 debugs(83, DBG_IMPORTANT
, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
229 debugs(83, DBG_IMPORTANT
, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg
.getBody());
230 validationResponse
->resultCode
= HelperReply::BrokenHelper
;
232 validationResponse
->resultCode
= reply
.result
;
234 crtdvdData
->callback(crtdvdData
->data
, *validationResponse
);
236 if (Ssl::CertValidationHelper::HelperCache
&&
237 (validationResponse
->resultCode
== HelperReply::Okay
|| validationResponse
->resultCode
== HelperReply::Error
)) {
238 Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
.c_str(), validationResponse
);
240 delete validationResponse
;
242 cbdataReferenceDone(crtdvdData
->data
);
243 SSL_free(crtdvdData
->ssl
);
247 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, Ssl::CertValidationHelper::CVHCB
* callback
, void * data
)
249 static time_t first_warn
= 0;
250 assert(ssl_crt_validator
);
252 if (ssl_crt_validator
->stats
.queue_size
>= (int)(ssl_crt_validator
->childs
.n_running
* 2)) {
254 first_warn
= squid_curtime
;
255 if (squid_curtime
- first_warn
> 3 * 60)
256 fatal("ssl_crtvd queue being overloaded for long time");
257 debugs(83, DBG_IMPORTANT
, "WARNING: ssl_crtvd queue overload, rejecting");
258 Ssl::CertValidationResponse resp
;
259 resp
.resultCode
= HelperReply::BrokenHelper
;
260 callback(data
, resp
);
265 Ssl::CertValidationMsg
message(Ssl::CrtdMessage::REQUEST
);
266 message
.setCode(Ssl::CertValidationMsg::code_cert_validate
);
267 message
.composeRequest(request
);
268 debugs(83, 5, "SSL crtvd request: " << message
.compose().c_str());
270 submitData
*crtdvdData
= new submitData
;
271 crtdvdData
->query
= message
.compose();
272 crtdvdData
->query
+= '\n';
273 crtdvdData
->callback
= callback
;
274 crtdvdData
->data
= cbdataReference(data
);
275 crtdvdData
->ssl
= request
.ssl
;
276 CRYPTO_add(&crtdvdData
->ssl
->references
,1,CRYPTO_LOCK_SSL
);
277 Ssl::CertValidationResponse
const*validationResponse
;
279 if (CertValidationHelper::HelperCache
&&
280 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
.c_str()))) {
281 callback(data
, *validationResponse
);
282 cbdataReferenceDone(crtdvdData
->data
);
283 SSL_free(crtdvdData
->ssl
);
287 helperSubmit(ssl_crt_validator
, crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
);