2 #include "anyp/PortCfg.h"
3 #include "ssl/Config.h"
4 #include "ssl/helper.h"
5 #include "SquidString.h"
8 #include "ssl/cert_validate_message.h"
10 #include "SquidConfig.h"
12 LruMap
<Ssl::CertValidationResponse
> *Ssl::CertValidationHelper::HelperCache
= NULL
;
15 Ssl::Helper
* Ssl::Helper::GetInstance()
17 static Ssl::Helper sslHelper
;
21 Ssl::Helper::Helper() : ssl_crtd(NULL
)
25 Ssl::Helper::~Helper()
30 void Ssl::Helper::Init()
32 assert(ssl_crtd
== NULL
);
34 // we need to start ssl_crtd only if some port(s) need to bump SSL
36 for (AnyP::PortCfg
*s
= ::Config
.Sockaddr
.http
; !found
&& s
; s
= s
->next
)
38 for (AnyP::PortCfg
*s
= ::Config
.Sockaddr
.https
; !found
&& s
; s
= s
->next
)
43 ssl_crtd
= new helper("ssl_crtd");
44 ssl_crtd
->childs
.updateLimits(Ssl::TheConfig
.ssl_crtdChildren
);
45 ssl_crtd
->ipc_type
= IPC_STREAM
;
46 // The crtd messages may contain the eol ('\n') character. We are
47 // going to use the '\1' char as the end-of-message mark.
49 assert(ssl_crtd
->cmdline
== NULL
);
51 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
52 char *tmp_begin
= tmp
;
54 bool db_path_was_found
= false;
55 bool block_size_was_found
= false;
56 char buffer
[20] = "2048";
57 while ((token
= strwordtok(NULL
, &tmp
))) {
58 wordlistAdd(&ssl_crtd
->cmdline
, token
);
59 if (!strcmp(token
, "-b"))
60 block_size_was_found
= true;
61 if (!strcmp(token
, "-s")) {
62 db_path_was_found
= true;
63 } else if (db_path_was_found
) {
64 db_path_was_found
= false;
65 int fs_block_size
= 0;
66 storeDirGetBlkSize(token
, &fs_block_size
);
67 snprintf(buffer
, sizeof(buffer
), "%i", fs_block_size
);
70 if (!block_size_was_found
) {
71 wordlistAdd(&ssl_crtd
->cmdline
, "-b");
72 wordlistAdd(&ssl_crtd
->cmdline
, buffer
);
76 helperOpenServers(ssl_crtd
);
79 void Ssl::Helper::Shutdown()
83 helperShutdown(ssl_crtd
);
84 wordlistDestroy(&ssl_crtd
->cmdline
);
89 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
91 static time_t first_warn
= 0;
94 if (ssl_crtd
->stats
.queue_size
>= (int)(ssl_crtd
->childs
.n_running
* 2)) {
96 first_warn
= squid_curtime
;
97 if (squid_curtime
- first_warn
> 3 * 60)
98 fatal("SSL servers not responding for 3 minutes");
99 debugs(34, DBG_IMPORTANT
, HERE
<< "Queue overload, rejecting");
100 HelperReply failReply
;
101 failReply
.result
= HelperReply::BrokenHelper
;
102 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
103 callback(data
, failReply
);
108 std::string msg
= message
.compose();
110 helperSubmit(ssl_crtd
, msg
.c_str(), callback
, data
);
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::PortCfg
*s
= ::Config
.Sockaddr
.http
; !found
&& s
; s
= s
->next
)
139 for (AnyP::PortCfg
*s
= ::Config
.Sockaddr
.https
; !found
&& s
; s
= s
->next
)
144 ssl_crt_validator
= new helper("ssl_crt_validator");
145 ssl_crt_validator
->childs
.updateLimits(Ssl::TheConfig
.ssl_crt_validator_Children
);
146 ssl_crt_validator
->ipc_type
= IPC_STREAM
;
147 // The crtd messages may contain the eol ('\n') character. We are
148 // going to use the '\1' char as the end-of-message mark.
149 ssl_crt_validator
->eom
= '\1';
150 assert(ssl_crt_validator
->cmdline
== NULL
);
155 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
156 char *tmp_begin
= tmp
;
158 bool parseParams
= true;
159 while ((token
= strwordtok(NULL
, &tmp
))) {
161 if (strncmp(token
, "ttl=", 4) == 0) {
162 ttl
= atoi(token
+ 4);
164 } else if (strncmp(token
, "cache=", 6) == 0) {
165 cache
= atoi(token
+ 6);
170 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
174 helperOpenServers(ssl_crt_validator
);
176 //WARNING: initializing static member in an object initialization method
177 assert(HelperCache
== NULL
);
178 HelperCache
= new LruMap
<Ssl::CertValidationResponse
>(ttl
, cache
);
181 void Ssl::CertValidationHelper::Shutdown()
183 if (!ssl_crt_validator
)
185 helperShutdown(ssl_crt_validator
);
186 wordlistDestroy(&ssl_crt_validator
->cmdline
);
187 delete ssl_crt_validator
;
188 ssl_crt_validator
= NULL
;
190 // CertValidationHelper::HelperCache is a static member, it is not good policy to
191 // reset it here. Will work because the current Ssl::CertValidationHelper is
192 // always the same static object.
199 Ssl::CertValidationHelper::CVHCB
*callback
;
202 CBDATA_CLASS2(submitData
);
204 CBDATA_CLASS_INIT(submitData
);
207 sslCrtvdHandleReplyWrapper(void *data
, const HelperReply
&reply
)
209 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
210 Ssl::CertValidationResponse
*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
== HelperReply::BrokenHelper
) {
216 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
217 validationResponse
->resultCode
= HelperReply::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
= HelperReply::BrokenHelper
;
224 validationResponse
->resultCode
= reply
.result
;
226 crtdvdData
->callback(crtdvdData
->data
, *validationResponse
);
228 if (Ssl::CertValidationHelper::HelperCache
&&
229 (validationResponse
->resultCode
== HelperReply::Okay
|| validationResponse
->resultCode
== HelperReply::Error
)) {
230 Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
.c_str(), validationResponse
);
232 delete validationResponse
;
234 cbdataReferenceDone(crtdvdData
->data
);
235 SSL_free(crtdvdData
->ssl
);
239 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, Ssl::CertValidationHelper::CVHCB
* callback
, void * data
)
241 static time_t first_warn
= 0;
242 assert(ssl_crt_validator
);
244 if (ssl_crt_validator
->stats
.queue_size
>= (int)(ssl_crt_validator
->childs
.n_running
* 2)) {
246 first_warn
= squid_curtime
;
247 if (squid_curtime
- first_warn
> 3 * 60)
248 fatal("ssl_crtvd queue being overloaded for long time");
249 debugs(83, DBG_IMPORTANT
, "WARNING: ssl_crtvd queue overload, rejecting");
250 Ssl::CertValidationResponse resp
;
251 resp
.resultCode
= HelperReply::BrokenHelper
;
252 callback(data
, resp
);
257 Ssl::CertValidationMsg
message(Ssl::CrtdMessage::REQUEST
);
258 message
.setCode(Ssl::CertValidationMsg::code_cert_validate
);
259 message
.composeRequest(request
);
260 debugs(83, 5, "SSL crtvd request: " << message
.compose().c_str());
262 submitData
*crtdvdData
= new submitData
;
263 crtdvdData
->query
= message
.compose();
264 crtdvdData
->query
+= '\n';
265 crtdvdData
->callback
= callback
;
266 crtdvdData
->data
= cbdataReference(data
);
267 crtdvdData
->ssl
= request
.ssl
;
268 CRYPTO_add(&crtdvdData
->ssl
->references
,1,CRYPTO_LOCK_SSL
);
269 Ssl::CertValidationResponse
const*validationResponse
;
271 if (CertValidationHelper::HelperCache
&&
272 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
.c_str()))) {
273 callback(data
, *validationResponse
);
274 cbdataReferenceDone(crtdvdData
->data
);
275 SSL_free(crtdvdData
->ssl
);
279 helperSubmit(ssl_crt_validator
, crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
);