]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/helper.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / ssl / helper.cc
1 /*
2 * Copyright (C) 1996-2016 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
9 #include "squid.h"
10 #include "../helper.h"
11 #include "anyp/PortCfg.h"
12 #include "fs_io.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"
20 #include "wordlist.h"
21
22 Ssl::CertValidationHelper::LruCache *Ssl::CertValidationHelper::HelperCache = nullptr;
23
24 #if USE_SSL_CRTD
25 Ssl::Helper * Ssl::Helper::GetInstance()
26 {
27 static Ssl::Helper sslHelper;
28 return &sslHelper;
29 }
30
31 Ssl::Helper::Helper() : ssl_crtd(NULL)
32 {
33 }
34
35 Ssl::Helper::~Helper()
36 {
37 Shutdown();
38 }
39
40 void Ssl::Helper::Init()
41 {
42 assert(ssl_crtd == NULL);
43
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
46 bool found = false;
47 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
48 found = s->flags.tunnelSslBumping && s->generateHostCertificates;
49 if (!found)
50 return;
51
52 ssl_crtd = new helper("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.
57 ssl_crtd->eom = '\1';
58 assert(ssl_crtd->cmdline == NULL);
59 {
60 char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
61 char *tmp_begin = tmp;
62 char * token = NULL;
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);
77 }
78 }
79 if (!block_size_was_found) {
80 wordlistAdd(&ssl_crtd->cmdline, "-b");
81 wordlistAdd(&ssl_crtd->cmdline, buffer);
82 }
83 safe_free(tmp_begin);
84 }
85 helperOpenServers(ssl_crtd);
86 }
87
88 void Ssl::Helper::Shutdown()
89 {
90 if (!ssl_crtd)
91 return;
92 helperShutdown(ssl_crtd);
93 wordlistDestroy(&ssl_crtd->cmdline);
94 delete ssl_crtd;
95 ssl_crtd = NULL;
96 }
97
98 void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
99 {
100 assert(ssl_crtd);
101
102 std::string msg = message.compose();
103 msg += '\n';
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);
109 return;
110 }
111 }
112 #endif //USE_SSL_CRTD
113
114 Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
115 {
116 static Ssl::CertValidationHelper sslHelper;
117 if (!Ssl::TheConfig.ssl_crt_validator)
118 return NULL;
119 return &sslHelper;
120 }
121
122 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL)
123 {
124 }
125
126 Ssl::CertValidationHelper::~CertValidationHelper()
127 {
128 Shutdown();
129 }
130
131 void Ssl::CertValidationHelper::Init()
132 {
133 assert(ssl_crt_validator == NULL);
134
135 // we need to start ssl_crtd only if some port(s) need to bump SSL
136 bool found = false;
137 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
138 found = s->flags.tunnelSslBumping;
139 if (!found)
140 return;
141
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);
149
150 int ttl = 60;
151 size_t cache = 2048;
152 {
153 char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
154 char *tmp_begin = tmp;
155 char * token = NULL;
156 bool parseParams = true;
157 while ((token = strwordtok(NULL, &tmp))) {
158 if (parseParams) {
159 if (strncmp(token, "ttl=", 4) == 0) {
160 ttl = atoi(token + 4);
161 continue;
162 } else if (strncmp(token, "cache=", 6) == 0) {
163 cache = atoi(token + 6);
164 continue;
165 } else
166 parseParams = false;
167 }
168 wordlistAdd(&ssl_crt_validator->cmdline, token);
169 }
170 xfree(tmp_begin);
171 }
172 helperOpenServers(ssl_crt_validator);
173
174 //WARNING: initializing static member in an object initialization method
175 assert(HelperCache == NULL);
176 HelperCache = new Ssl::CertValidationHelper::LruCache(ttl, cache);
177 }
178
179 void Ssl::CertValidationHelper::Shutdown()
180 {
181 if (!ssl_crt_validator)
182 return;
183 helperShutdown(ssl_crt_validator);
184 wordlistDestroy(&ssl_crt_validator->cmdline);
185 delete ssl_crt_validator;
186 ssl_crt_validator = NULL;
187
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.
191 delete HelperCache;
192 HelperCache = NULL;
193 }
194
195 class submitData
196 {
197 CBDATA_CLASS(submitData);
198
199 public:
200 std::string query;
201 AsyncCall::Pointer callback;
202 SSL *ssl;
203 };
204 CBDATA_CLASS_INIT(submitData);
205
206 static void
207 sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
208 {
209 Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
210 Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse;
211 std::string error;
212
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;
223 } else
224 validationResponse->resultCode = reply.result;
225
226 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(crtdvdData->callback->getDialer());
227 Must(dialer);
228 dialer->arg1 = validationResponse;
229 ScheduleCallHere(crtdvdData->callback);
230
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);
235 }
236
237 SSL_free(crtdvdData->ssl);
238 delete crtdvdData;
239 }
240
241 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
242 {
243 assert(ssl_crt_validator);
244
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());
249
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;
257
258 if (CertValidationHelper::HelperCache &&
259 (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
260
261 CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
262 dialer->arg1 = *validationResponse;
263 ScheduleCallHere(callback);
264 SSL_free(crtdvdData->ssl);
265 delete crtdvdData;
266 return;
267 }
268
269 if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) {
270 Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse;;
271 resp->resultCode = ::Helper::BrokenHelper;
272 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
273 dialer->arg1 = resp;
274 ScheduleCallHere(callback);
275
276 SSL_free(crtdvdData->ssl);
277 delete crtdvdData;
278 return;
279 }
280 }
281