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 "../helper.h"
11 #include "anyp/PortCfg.h"
12 #include "helper/Reply.h"
13 #include "SquidConfig.h"
14 #include "SquidString.h"
15 #include "SquidTime.h"
16 #include "ssl/cert_validate_message.h"
17 #include "ssl/Config.h"
18 #include "ssl/helper.h"
22 LruMap
<Ssl::CertValidationResponse
> *Ssl::CertValidationHelper::HelperCache
= NULL
;
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
46 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
47 found
= s
->flags
.tunnelSslBumping
;
48 for (AnyP::PortCfgPointer s
= HttpsPortList
; !found
&& s
!= NULL
; s
= s
->next
)
49 found
= s
->flags
.tunnelSslBumping
;
53 ssl_crtd
= new helper("ssl_crtd");
54 ssl_crtd
->childs
.updateLimits(Ssl::TheConfig
.ssl_crtdChildren
);
55 ssl_crtd
->ipc_type
= IPC_STREAM
;
56 // The crtd messages may contain the eol ('\n') character. We are
57 // going to use the '\1' char as the end-of-message mark.
59 assert(ssl_crtd
->cmdline
== NULL
);
61 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crtd
);
62 char *tmp_begin
= tmp
;
64 bool db_path_was_found
= false;
65 bool block_size_was_found
= false;
66 char buffer
[20] = "2048";
67 while ((token
= strwordtok(NULL
, &tmp
))) {
68 wordlistAdd(&ssl_crtd
->cmdline
, token
);
69 if (!strcmp(token
, "-b"))
70 block_size_was_found
= true;
71 if (!strcmp(token
, "-s")) {
72 db_path_was_found
= true;
73 } else if (db_path_was_found
) {
74 db_path_was_found
= false;
75 int fs_block_size
= 0;
76 storeDirGetBlkSize(token
, &fs_block_size
);
77 snprintf(buffer
, sizeof(buffer
), "%i", fs_block_size
);
80 if (!block_size_was_found
) {
81 wordlistAdd(&ssl_crtd
->cmdline
, "-b");
82 wordlistAdd(&ssl_crtd
->cmdline
, buffer
);
86 helperOpenServers(ssl_crtd
);
89 void Ssl::Helper::Shutdown()
93 helperShutdown(ssl_crtd
);
94 wordlistDestroy(&ssl_crtd
->cmdline
);
99 void Ssl::Helper::sslSubmit(CrtdMessage
const & message
, HLPCB
* callback
, void * data
)
103 std::string msg
= message
.compose();
105 if (!ssl_crtd
->trySubmit(msg
.c_str(), callback
, data
)) {
106 ::Helper::Reply failReply
;
107 failReply
.result
= ::Helper::BrokenHelper
;
108 failReply
.notes
.add("message", "error 45 Temporary network problem, please retry later");
109 callback(data
, failReply
);
113 #endif //USE_SSL_CRTD
115 Ssl::CertValidationHelper
* Ssl::CertValidationHelper::GetInstance()
117 static Ssl::CertValidationHelper sslHelper
;
118 if (!Ssl::TheConfig
.ssl_crt_validator
)
123 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL
)
127 Ssl::CertValidationHelper::~CertValidationHelper()
132 void Ssl::CertValidationHelper::Init()
134 assert(ssl_crt_validator
== NULL
);
136 // we need to start ssl_crtd only if some port(s) need to bump SSL
138 for (AnyP::PortCfgPointer s
= HttpPortList
; !found
&& s
!= NULL
; s
= s
->next
)
139 found
= s
->flags
.tunnelSslBumping
;
140 for (AnyP::PortCfgPointer s
= HttpsPortList
; !found
&& s
!= NULL
; s
= s
->next
)
141 found
= s
->flags
.tunnelSslBumping
;
145 ssl_crt_validator
= new helper("ssl_crt_validator");
146 ssl_crt_validator
->childs
.updateLimits(Ssl::TheConfig
.ssl_crt_validator_Children
);
147 ssl_crt_validator
->ipc_type
= IPC_STREAM
;
148 // The crtd messages may contain the eol ('\n') character. We are
149 // going to use the '\1' char as the end-of-message mark.
150 ssl_crt_validator
->eom
= '\1';
151 assert(ssl_crt_validator
->cmdline
== NULL
);
156 char *tmp
= xstrdup(Ssl::TheConfig
.ssl_crt_validator
);
157 char *tmp_begin
= tmp
;
159 bool parseParams
= true;
160 while ((token
= strwordtok(NULL
, &tmp
))) {
162 if (strncmp(token
, "ttl=", 4) == 0) {
163 ttl
= atoi(token
+ 4);
165 } else if (strncmp(token
, "cache=", 6) == 0) {
166 cache
= atoi(token
+ 6);
171 wordlistAdd(&ssl_crt_validator
->cmdline
, token
);
175 helperOpenServers(ssl_crt_validator
);
177 //WARNING: initializing static member in an object initialization method
178 assert(HelperCache
== NULL
);
179 HelperCache
= new LruMap
<Ssl::CertValidationResponse
>(ttl
, cache
);
182 void Ssl::CertValidationHelper::Shutdown()
184 if (!ssl_crt_validator
)
186 helperShutdown(ssl_crt_validator
);
187 wordlistDestroy(&ssl_crt_validator
->cmdline
);
188 delete ssl_crt_validator
;
189 ssl_crt_validator
= NULL
;
191 // CertValidationHelper::HelperCache is a static member, it is not good policy to
192 // reset it here. Will work because the current Ssl::CertValidationHelper is
193 // always the same static object.
200 CBDATA_CLASS(submitData
);
204 Ssl::CertValidationHelper::CVHCB
*callback
;
208 CBDATA_CLASS_INIT(submitData
);
211 sslCrtvdHandleReplyWrapper(void *data
, const ::Helper::Reply
&reply
)
213 Ssl::CertValidationMsg
replyMsg(Ssl::CrtdMessage::REPLY
);
214 Ssl::CertValidationResponse
*validationResponse
= new Ssl::CertValidationResponse
;
217 submitData
*crtdvdData
= static_cast<submitData
*>(data
);
218 STACK_OF(X509
) *peerCerts
= SSL_get_peer_cert_chain(crtdvdData
->ssl
);
219 if (reply
.result
== ::Helper::BrokenHelper
) {
220 debugs(83, DBG_IMPORTANT
, "\"ssl_crtvd\" helper error response: " << reply
.other().content());
221 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
222 } else if (replyMsg
.parse(reply
.other().content(), reply
.other().contentSize()) != Ssl::CrtdMessage::OK
||
223 !replyMsg
.parseResponse(*validationResponse
, peerCerts
, error
) ) {
224 debugs(83, DBG_IMPORTANT
, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
225 debugs(83, DBG_IMPORTANT
, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg
.getBody());
226 validationResponse
->resultCode
= ::Helper::BrokenHelper
;
228 validationResponse
->resultCode
= reply
.result
;
230 crtdvdData
->callback(crtdvdData
->data
, *validationResponse
);
232 if (Ssl::CertValidationHelper::HelperCache
&&
233 (validationResponse
->resultCode
== ::Helper::Okay
|| validationResponse
->resultCode
== ::Helper::Error
)) {
234 Ssl::CertValidationHelper::HelperCache
->add(crtdvdData
->query
.c_str(), validationResponse
);
236 delete validationResponse
;
238 cbdataReferenceDone(crtdvdData
->data
);
239 SSL_free(crtdvdData
->ssl
);
243 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest
const &request
, Ssl::CertValidationHelper::CVHCB
* callback
, void * data
)
245 assert(ssl_crt_validator
);
247 Ssl::CertValidationMsg
message(Ssl::CrtdMessage::REQUEST
);
248 message
.setCode(Ssl::CertValidationMsg::code_cert_validate
);
249 message
.composeRequest(request
);
250 debugs(83, 5, "SSL crtvd request: " << message
.compose().c_str());
252 submitData
*crtdvdData
= new submitData
;
253 crtdvdData
->query
= message
.compose();
254 crtdvdData
->query
+= '\n';
255 crtdvdData
->callback
= callback
;
256 crtdvdData
->data
= cbdataReference(data
);
257 crtdvdData
->ssl
= request
.ssl
;
258 CRYPTO_add(&crtdvdData
->ssl
->references
,1,CRYPTO_LOCK_SSL
);
259 Ssl::CertValidationResponse
const*validationResponse
;
261 if (CertValidationHelper::HelperCache
&&
262 (validationResponse
= CertValidationHelper::HelperCache
->get(crtdvdData
->query
.c_str()))) {
263 callback(data
, *validationResponse
);
264 cbdataReferenceDone(crtdvdData
->data
);
265 SSL_free(crtdvdData
->ssl
);
270 if (!ssl_crt_validator
->trySubmit(crtdvdData
->query
.c_str(), sslCrtvdHandleReplyWrapper
, crtdvdData
)) {
271 Ssl::CertValidationResponse resp
;
272 resp
.resultCode
= ::Helper::BrokenHelper
;
273 callback(data
, resp
);
275 cbdataReferenceDone(crtdvdData
->data
);
276 SSL_free(crtdvdData
->ssl
);