]> 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-2017 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::TheConfig.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 while ((token = strwordtok(NULL, &tmp))) {
64 wordlistAdd(&ssl_crtd->cmdline, token);
65 }
66 safe_free(tmp_begin);
67 }
68 helperOpenServers(ssl_crtd);
69 }
70
71 void Ssl::Helper::Shutdown()
72 {
73 if (!ssl_crtd)
74 return;
75 helperShutdown(ssl_crtd);
76 wordlistDestroy(&ssl_crtd->cmdline);
77 delete ssl_crtd;
78 ssl_crtd = NULL;
79 }
80
81 void Ssl::Helper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
82 {
83 assert(ssl_crtd);
84
85 std::string msg = message.compose();
86 msg += '\n';
87 if (!ssl_crtd->trySubmit(msg.c_str(), callback, data)) {
88 ::Helper::Reply failReply(::Helper::BrokenHelper);
89 failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
90 callback(data, failReply);
91 return;
92 }
93 }
94 #endif //USE_SSL_CRTD
95
96 Ssl::CertValidationHelper * Ssl::CertValidationHelper::GetInstance()
97 {
98 static Ssl::CertValidationHelper sslHelper;
99 if (!Ssl::TheConfig.ssl_crt_validator)
100 return NULL;
101 return &sslHelper;
102 }
103
104 Ssl::CertValidationHelper::CertValidationHelper() : ssl_crt_validator(NULL)
105 {
106 }
107
108 Ssl::CertValidationHelper::~CertValidationHelper()
109 {
110 Shutdown();
111 }
112
113 void Ssl::CertValidationHelper::Init()
114 {
115 assert(ssl_crt_validator == NULL);
116
117 // we need to start ssl_crtd only if some port(s) need to bump SSL
118 bool found = false;
119 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != NULL; s = s->next)
120 found = s->flags.tunnelSslBumping;
121 if (!found)
122 return;
123
124 ssl_crt_validator = new helper("ssl_crt_validator");
125 ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children);
126 ssl_crt_validator->ipc_type = IPC_STREAM;
127 // The crtd messages may contain the eol ('\n') character. We are
128 // going to use the '\1' char as the end-of-message mark.
129 ssl_crt_validator->eom = '\1';
130 assert(ssl_crt_validator->cmdline == NULL);
131
132 int ttl = 60;
133 size_t cache = 2048;
134 {
135 char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
136 char *tmp_begin = tmp;
137 char * token = NULL;
138 bool parseParams = true;
139 while ((token = strwordtok(NULL, &tmp))) {
140 if (parseParams) {
141 if (strncmp(token, "ttl=", 4) == 0) {
142 ttl = atoi(token + 4);
143 continue;
144 } else if (strncmp(token, "cache=", 6) == 0) {
145 cache = atoi(token + 6);
146 continue;
147 } else
148 parseParams = false;
149 }
150 wordlistAdd(&ssl_crt_validator->cmdline, token);
151 }
152 xfree(tmp_begin);
153 }
154 helperOpenServers(ssl_crt_validator);
155
156 //WARNING: initializing static member in an object initialization method
157 assert(HelperCache == NULL);
158 HelperCache = new Ssl::CertValidationHelper::LruCache(ttl, cache);
159 }
160
161 void Ssl::CertValidationHelper::Shutdown()
162 {
163 if (!ssl_crt_validator)
164 return;
165 helperShutdown(ssl_crt_validator);
166 wordlistDestroy(&ssl_crt_validator->cmdline);
167 delete ssl_crt_validator;
168 ssl_crt_validator = NULL;
169
170 // CertValidationHelper::HelperCache is a static member, it is not good policy to
171 // reset it here. Will work because the current Ssl::CertValidationHelper is
172 // always the same static object.
173 delete HelperCache;
174 HelperCache = NULL;
175 }
176
177 class submitData
178 {
179 CBDATA_CLASS(submitData);
180
181 public:
182 std::string query;
183 AsyncCall::Pointer callback;
184 Security::SessionPointer ssl;
185 };
186 CBDATA_CLASS_INIT(submitData);
187
188 static void
189 sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
190 {
191 Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
192 Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse;
193 std::string error;
194
195 submitData *crtdvdData = static_cast<submitData *>(data);
196 STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl.get());
197 if (reply.result == ::Helper::BrokenHelper) {
198 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
199 validationResponse->resultCode = ::Helper::BrokenHelper;
200 } else if (!reply.other().hasContent()) {
201 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response");
202 validationResponse->resultCode = ::Helper::BrokenHelper;
203 } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
204 !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) {
205 debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
206 debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
207 validationResponse->resultCode = ::Helper::BrokenHelper;
208 } else
209 validationResponse->resultCode = reply.result;
210
211 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(crtdvdData->callback->getDialer());
212 Must(dialer);
213 dialer->arg1 = validationResponse;
214 ScheduleCallHere(crtdvdData->callback);
215
216 if (Ssl::CertValidationHelper::HelperCache &&
217 (validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
218 Ssl::CertValidationResponse::Pointer *item = new Ssl::CertValidationResponse::Pointer(validationResponse);
219 if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), item))
220 delete item;
221 }
222
223 delete crtdvdData;
224 }
225
226 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
227 {
228 assert(ssl_crt_validator);
229
230 Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
231 message.setCode(Ssl::CertValidationMsg::code_cert_validate);
232 message.composeRequest(request);
233 debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
234
235 submitData *crtdvdData = new submitData;
236 crtdvdData->query = message.compose();
237 crtdvdData->query += '\n';
238 crtdvdData->callback = callback;
239 crtdvdData->ssl.resetAndLock(request.ssl);
240 Ssl::CertValidationResponse::Pointer const*validationResponse;
241
242 if (CertValidationHelper::HelperCache &&
243 (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
244
245 CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
246 Must(dialer);
247 dialer->arg1 = *validationResponse;
248 ScheduleCallHere(callback);
249 delete crtdvdData;
250 return;
251 }
252
253 if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) {
254 Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse;;
255 resp->resultCode = ::Helper::BrokenHelper;
256 Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
257 Must(dialer);
258 dialer->arg1 = resp;
259 ScheduleCallHere(callback);
260 delete crtdvdData;
261 return;
262 }
263 }
264