]>
Commit | Line | Data |
---|---|---|
bbc27441 AJ |
1 | /* |
2 | * Copyright (C) 1996-2014 The Squid Software Foundation and contributors | |
3 | * | |
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. | |
7 | */ | |
8 | ||
f7f3304a | 9 | #include "squid.h" |
18f37f42 | 10 | #include "anyp/PortCfg.h" |
602d9612 | 11 | #include "SquidConfig.h" |
f9b6ff6e | 12 | #include "SquidString.h" |
95d2589c | 13 | #include "SquidTime.h" |
14798e73 | 14 | #include "ssl/cert_validate_message.h" |
602d9612 A |
15 | #include "ssl/Config.h" |
16 | #include "ssl/helper.h" | |
17 | #include "SwapDir.h" | |
fc54b8d2 | 18 | #include "wordlist.h" |
95d2589c | 19 | |
14798e73 CT |
20 | LruMap<Ssl::CertValidationResponse> *Ssl::CertValidationHelper::HelperCache = NULL; |
21 | ||
4a77bb4e | 22 | #if USE_SSL_CRTD |
95d2589c CT |
23 | Ssl::Helper * Ssl::Helper::GetInstance() |
24 | { | |
25 | static Ssl::Helper sslHelper; | |
26 | return &sslHelper; | |
27 | } | |
28 | ||
fad2588a | 29 | Ssl::Helper::Helper() : ssl_crtd(NULL) |
95d2589c | 30 | { |
95d2589c CT |
31 | } |
32 | ||
33 | Ssl::Helper::~Helper() | |
34 | { | |
35 | Shutdown(); | |
36 | } | |
37 | ||
38 | void Ssl::Helper::Init() | |
39 | { | |
586089cd CT |
40 | assert(ssl_crtd == NULL); |
41 | ||
24e6c8f1 AR |
42 | // we need to start ssl_crtd only if some port(s) need to bump SSL |
43 | bool found = false; | |
fa720bfb | 44 | for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next) |
6a25a046 | 45 | found = s->flags.tunnelSslBumping; |
fa720bfb | 46 | for (AnyP::PortCfgPointer s = HttpsPortList; !found && s != NULL; s = s->next) |
6a25a046 | 47 | found = s->flags.tunnelSslBumping; |
24e6c8f1 | 48 | if (!found) |
586089cd CT |
49 | return; |
50 | ||
51 | ssl_crtd = new helper("ssl_crtd"); | |
1af735c7 | 52 | ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren); |
95d2589c | 53 | ssl_crtd->ipc_type = IPC_STREAM; |
0af9303a CT |
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. | |
56 | ssl_crtd->eom = '\1'; | |
95d2589c CT |
57 | assert(ssl_crtd->cmdline == NULL); |
58 | { | |
59 | char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd); | |
60 | char *tmp_begin = tmp; | |
61 | char * token = NULL; | |
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); | |
76 | } | |
77 | } | |
78 | if (!block_size_was_found) { | |
79 | wordlistAdd(&ssl_crtd->cmdline, "-b"); | |
80 | wordlistAdd(&ssl_crtd->cmdline, buffer); | |
81 | } | |
82 | safe_free(tmp_begin); | |
83 | } | |
95d2589c CT |
84 | helperOpenServers(ssl_crtd); |
85 | } | |
86 | ||
87 | void Ssl::Helper::Shutdown() | |
88 | { | |
89 | if (!ssl_crtd) | |
90 | return; | |
91 | helperShutdown(ssl_crtd); | |
92 | wordlistDestroy(&ssl_crtd->cmdline); | |
95d2589c CT |
93 | delete ssl_crtd; |
94 | ssl_crtd = NULL; | |
95 | } | |
96 | ||
97 | void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data) | |
98 | { | |
99 | static time_t first_warn = 0; | |
586089cd | 100 | assert(ssl_crtd); |
95d2589c CT |
101 | |
102 | if (ssl_crtd->stats.queue_size >= (int)(ssl_crtd->childs.n_running * 2)) { | |
103 | if (first_warn == 0) | |
104 | first_warn = squid_curtime; | |
105 | if (squid_curtime - first_warn > 3 * 60) | |
106 | fatal("SSL servers not responding for 3 minutes"); | |
e0236918 | 107 | debugs(34, DBG_IMPORTANT, HERE << "Queue overload, rejecting"); |
3c2c35d3 AJ |
108 | HelperReply failReply; |
109 | failReply.result = HelperReply::BrokenHelper; | |
fd7f26ea | 110 | failReply.notes.add("message", "error 45 Temporary network problem, please retry later"); |
12f7e09b | 111 | callback(data, failReply); |
95d2589c CT |
112 | return; |
113 | } | |
114 | ||
115 | first_warn = 0; | |
0af9303a CT |
116 | std::string msg = message.compose(); |
117 | msg += '\n'; | |
118 | helperSubmit(ssl_crtd, msg.c_str(), callback, data); | |
95d2589c | 119 | } |
4a77bb4e | 120 | #endif //USE_SSL_CRTD |
2cef0ca6 | 121 | |
2cef0ca6 AR |
122 | Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance() |
123 | { | |
124 | static Ssl::CertValidationHelper sslHelper; | |
125 | if (!Ssl::TheConfig.ssl_crt_validator) | |
126 | return NULL; | |
127 | return &sslHelper; | |
128 | } | |
129 | ||
130 | Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL) | |
131 | { | |
132 | } | |
133 | ||
134 | Ssl::CertValidationHelper::~CertValidationHelper() | |
135 | { | |
136 | Shutdown(); | |
137 | } | |
138 | ||
139 | void Ssl::CertValidationHelper::Init() | |
140 | { | |
141 | assert(ssl_crt_validator == NULL); | |
142 | ||
143 | // we need to start ssl_crtd only if some port(s) need to bump SSL | |
144 | bool found = false; | |
fa720bfb | 145 | for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next) |
6a25a046 | 146 | found = s->flags.tunnelSslBumping; |
fa720bfb | 147 | for (AnyP::PortCfgPointer s = HttpsPortList; !found && s != NULL; s = s->next) |
6a25a046 | 148 | found = s->flags.tunnelSslBumping; |
2cef0ca6 AR |
149 | if (!found) |
150 | return; | |
151 | ||
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); | |
14798e73 CT |
159 | |
160 | int ttl = 60; | |
161 | size_t cache = 2048; | |
2cef0ca6 AR |
162 | { |
163 | char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator); | |
164 | char *tmp_begin = tmp; | |
165 | char * token = NULL; | |
14798e73 | 166 | bool parseParams = true; |
2cef0ca6 | 167 | while ((token = strwordtok(NULL, &tmp))) { |
14798e73 CT |
168 | if (parseParams) { |
169 | if (strncmp(token, "ttl=", 4) == 0) { | |
170 | ttl = atoi(token + 4); | |
171 | continue; | |
172 | } else if (strncmp(token, "cache=", 6) == 0) { | |
173 | cache = atoi(token + 6); | |
174 | continue; | |
175 | } else | |
176 | parseParams = false; | |
177 | } | |
2cef0ca6 AR |
178 | wordlistAdd(&ssl_crt_validator->cmdline, token); |
179 | } | |
4a77bb4e | 180 | xfree(tmp_begin); |
2cef0ca6 AR |
181 | } |
182 | helperOpenServers(ssl_crt_validator); | |
14798e73 CT |
183 | |
184 | //WARNING: initializing static member in an object initialization method | |
185 | assert(HelperCache == NULL); | |
186 | HelperCache = new LruMap<Ssl::CertValidationResponse>(ttl, cache); | |
2cef0ca6 AR |
187 | } |
188 | ||
189 | void Ssl::CertValidationHelper::Shutdown() | |
190 | { | |
191 | if (!ssl_crt_validator) | |
192 | return; | |
193 | helperShutdown(ssl_crt_validator); | |
194 | wordlistDestroy(&ssl_crt_validator->cmdline); | |
195 | delete ssl_crt_validator; | |
196 | ssl_crt_validator = NULL; | |
4c304fb9 | 197 | |
14798e73 | 198 | // CertValidationHelper::HelperCache is a static member, it is not good policy to |
4c304fb9 | 199 | // reset it here. Will work because the current Ssl::CertValidationHelper is |
14798e73 CT |
200 | // always the same static object. |
201 | delete HelperCache; | |
202 | HelperCache = NULL; | |
203 | } | |
204 | ||
205 | struct submitData { | |
206 | std::string query; | |
207 | Ssl::CertValidationHelper::CVHCB *callback; | |
208 | void *data; | |
209 | SSL *ssl; | |
210 | CBDATA_CLASS2(submitData); | |
211 | }; | |
212 | CBDATA_CLASS_INIT(submitData); | |
213 | ||
214 | static void | |
215 | sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply) | |
216 | { | |
217 | Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY); | |
218 | Ssl::CertValidationResponse *validationResponse = new Ssl::CertValidationResponse; | |
219 | std::string error; | |
220 | ||
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 || | |
4c304fb9 | 227 | !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) { |
14798e73 CT |
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; | |
4c304fb9 | 231 | } else |
14798e73 CT |
232 | validationResponse->resultCode = reply.result; |
233 | ||
234 | crtdvdData->callback(crtdvdData->data, *validationResponse); | |
235 | ||
236 | if (Ssl::CertValidationHelper::HelperCache && | |
4c304fb9 | 237 | (validationResponse->resultCode == HelperReply::Okay || validationResponse->resultCode == HelperReply::Error)) { |
14798e73 CT |
238 | Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), validationResponse); |
239 | } else | |
240 | delete validationResponse; | |
241 | ||
242 | cbdataReferenceDone(crtdvdData->data); | |
243 | SSL_free(crtdvdData->ssl); | |
244 | delete crtdvdData; | |
2cef0ca6 AR |
245 | } |
246 | ||
14798e73 | 247 | void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, Ssl::CertValidationHelper::CVHCB * callback, void * data) |
2cef0ca6 AR |
248 | { |
249 | static time_t first_warn = 0; | |
250 | assert(ssl_crt_validator); | |
251 | ||
252 | if (ssl_crt_validator->stats.queue_size >= (int)(ssl_crt_validator->childs.n_running * 2)) { | |
253 | if (first_warn == 0) | |
254 | first_warn = squid_curtime; | |
255 | if (squid_curtime - first_warn > 3 * 60) | |
4a77bb4e CT |
256 | fatal("ssl_crtvd queue being overloaded for long time"); |
257 | debugs(83, DBG_IMPORTANT, "WARNING: ssl_crtvd queue overload, rejecting"); | |
14798e73 CT |
258 | Ssl::CertValidationResponse resp; |
259 | resp.resultCode = HelperReply::BrokenHelper; | |
260 | callback(data, resp); | |
2cef0ca6 AR |
261 | return; |
262 | } | |
2cef0ca6 | 263 | first_warn = 0; |
14798e73 CT |
264 | |
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()); | |
269 | ||
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; | |
278 | ||
279 | if (CertValidationHelper::HelperCache && | |
4c304fb9 | 280 | (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) { |
14798e73 CT |
281 | callback(data, *validationResponse); |
282 | cbdataReferenceDone(crtdvdData->data); | |
283 | SSL_free(crtdvdData->ssl); | |
284 | delete crtdvdData; | |
285 | return; | |
286 | } | |
287 | helperSubmit(ssl_crt_validator, crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData); | |
2cef0ca6 | 288 | } |