]> git.ipfire.org Git - thirdparty/squid.git/blame - src/ssl/helper.cc
Fix SQUID_YESNO 'syntax error near unexpected token' (#2117)
[thirdparty/squid.git] / src / ssl / helper.cc
CommitLineData
bbc27441 1/*
1f7b830e 2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
bbc27441
AJ
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"
24438ec5 10#include "../helper.h"
502bcc7b 11#include "anyp/PortCfg.h"
e5ddd4ce 12#include "base/AsyncCallbacks.h"
72247610 13#include "cache_cf.h"
b3f7fd88 14#include "fs_io.h"
24438ec5 15#include "helper/Reply.h"
72247610 16#include "Parsing.h"
70ac5b29 17#include "sbuf/Stream.h"
602d9612 18#include "SquidConfig.h"
14798e73 19#include "ssl/cert_validate_message.h"
602d9612
A
20#include "ssl/Config.h"
21#include "ssl/helper.h"
fc54b8d2 22#include "wordlist.h"
95d2589c 23
1f8b5f0e 24#include <limits>
25
72247610 26Ssl::CertValidationHelper::CacheType *Ssl::CertValidationHelper::HelperCache = nullptr;
968b000d 27
58fa3f51
CT
28#if USE_SSL_CRTD
29
30namespace Ssl {
31
32/// Initiator of an Ssl::Helper query.
33class GeneratorRequestor {
34public:
35 GeneratorRequestor(HLPCB *aCallback, void *aData): callback(aCallback), data(aData) {}
36 HLPCB *callback;
37 CallbackData data;
38};
39
40/// A pending Ssl::Helper request, combining the original and collapsed queries.
41class GeneratorRequest {
42 CBDATA_CLASS(GeneratorRequest);
43
44public:
45 /// adds a GeneratorRequestor
46 void emplace(HLPCB *callback, void *data) { requestors.emplace_back(callback, data); }
47
48 SBuf query; ///< Ssl::Helper request message (GeneratorRequests key)
49
50 /// Ssl::Helper request initiators waiting for the same answer (FIFO).
51 typedef std::vector<GeneratorRequestor> GeneratorRequestors;
52 GeneratorRequestors requestors;
53};
54
55/// Ssl::Helper query:GeneratorRequest map
56typedef std::unordered_map<SBuf, GeneratorRequest*> GeneratorRequests;
57
58static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply);
59
9e779e40
EB
60/// pending Ssl::Helper requests (to all certificate generator helpers combined)
61static GeneratorRequests &
62TheGeneratorRequests()
63{
64 static auto generatorRequests = new GeneratorRequests();
65 return *generatorRequests;
66}
67
58fa3f51
CT
68} // namespace Ssl
69
70CBDATA_NAMESPACED_CLASS_INIT(Ssl, GeneratorRequest);
71
72/// prints Ssl::GeneratorRequest for debugging
73static std::ostream &
74operator <<(std::ostream &os, const Ssl::GeneratorRequest &gr)
75{
76 return os << "crtGenRq" << gr.query.id.value << "/" << gr.requestors.size();
77}
78
e05a9d51 79Helper::Client::Pointer Ssl::Helper::ssl_crtd;
95d2589c
CT
80
81void Ssl::Helper::Init()
82{
aee3523a 83 assert(ssl_crtd == nullptr);
586089cd 84
f0763147
AJ
85 // we need to start ssl_crtd only if some port(s) need to bump SSL *and* generate certificates
86 // TODO: generate host certificates for SNI enabled accel ports
24e6c8f1 87 bool found = false;
aee3523a 88 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != nullptr; s = s->next)
cf487124 89 found = s->flags.tunnelSslBumping && s->secure.generateHostCertificates;
24e6c8f1 90 if (!found)
586089cd
CT
91 return;
92
e05a9d51 93 ssl_crtd = ::Helper::Client::Make("sslcrtd_program");
1af735c7 94 ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren);
95d2589c 95 ssl_crtd->ipc_type = IPC_STREAM;
0af9303a
CT
96 // The crtd messages may contain the eol ('\n') character. We are
97 // going to use the '\1' char as the end-of-message mark.
98 ssl_crtd->eom = '\1';
aee3523a 99 assert(ssl_crtd->cmdline == nullptr);
95d2589c
CT
100 {
101 char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
102 char *tmp_begin = tmp;
aee3523a
AR
103 char *token = nullptr;
104 while ((token = strwordtok(nullptr, &tmp))) {
95d2589c 105 wordlistAdd(&ssl_crtd->cmdline, token);
95d2589c
CT
106 }
107 safe_free(tmp_begin);
108 }
bd71920d 109 ssl_crtd->openSessions();
95d2589c
CT
110}
111
112void Ssl::Helper::Shutdown()
113{
114 if (!ssl_crtd)
115 return;
116 helperShutdown(ssl_crtd);
117 wordlistDestroy(&ssl_crtd->cmdline);
aee3523a 118 ssl_crtd = nullptr;
95d2589c
CT
119}
120
23da195f
CT
121void
122Ssl::Helper::Reconfigure()
95d2589c 123{
23da195f
CT
124 Shutdown();
125 Init();
126}
95d2589c 127
23da195f
CT
128void Ssl::Helper::Submit(CrtdMessage const & message, HLPCB * callback, void * data)
129{
58fa3f51
CT
130 SBuf rawMessage(message.compose().c_str()); // XXX: helpers cannot use SBuf
131 rawMessage.append("\n", 1);
132
9e779e40
EB
133 const auto pending = TheGeneratorRequests().find(rawMessage);
134 if (pending != TheGeneratorRequests().end()) {
58fa3f51
CT
135 pending->second->emplace(callback, data);
136 debugs(83, 5, "collapsed request from " << data << " onto " << *pending->second);
137 return;
138 }
139
140 GeneratorRequest *request = new GeneratorRequest;
141 request->query = rawMessage;
142 request->emplace(callback, data);
9e779e40 143 TheGeneratorRequests().emplace(request->query, request);
58fa3f51 144 debugs(83, 5, "request from " << data << " as " << *request);
23da195f
CT
145 // ssl_crtd becomes nil if Squid is reconfigured without SslBump or
146 // certificate generation disabled in the new configuration
147 if (ssl_crtd && ssl_crtd->trySubmit(request->query.c_str(), HandleGeneratorReply, request))
95d2589c 148 return;
58fa3f51
CT
149
150 ::Helper::Reply failReply(::Helper::BrokenHelper);
151 failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
152 HandleGeneratorReply(request, failReply);
153}
154
155/// receives helper response
156static void
157Ssl::HandleGeneratorReply(void *data, const ::Helper::Reply &reply)
158{
159 const std::unique_ptr<Ssl::GeneratorRequest> request(static_cast<Ssl::GeneratorRequest*>(data));
160 assert(request);
9e779e40 161 const auto erased = TheGeneratorRequests().erase(request->query);
58fa3f51
CT
162 assert(erased);
163
164 for (auto &requestor: request->requestors) {
165 if (void *cbdata = requestor.data.validDone()) {
166 debugs(83, 5, "to " << cbdata << " in " << *request);
167 requestor.callback(cbdata, reply);
168 }
95d2589c 169 }
95d2589c 170}
4a77bb4e 171#endif //USE_SSL_CRTD
2cef0ca6 172
e05a9d51 173Helper::Client::Pointer Ssl::CertValidationHelper::ssl_crt_validator;
2cef0ca6
AR
174
175void Ssl::CertValidationHelper::Init()
176{
23da195f
CT
177 if (!Ssl::TheConfig.ssl_crt_validator)
178 return;
179
aee3523a 180 assert(ssl_crt_validator == nullptr);
2cef0ca6
AR
181
182 // we need to start ssl_crtd only if some port(s) need to bump SSL
183 bool found = false;
aee3523a 184 for (AnyP::PortCfgPointer s = HttpPortList; !found && s != nullptr; s = s->next)
6a25a046 185 found = s->flags.tunnelSslBumping;
2cef0ca6
AR
186 if (!found)
187 return;
188
e05a9d51 189 ssl_crt_validator = ::Helper::Client::Make("ssl_crt_validator");
2cef0ca6
AR
190 ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children);
191 ssl_crt_validator->ipc_type = IPC_STREAM;
192 // The crtd messages may contain the eol ('\n') character. We are
193 // going to use the '\1' char as the end-of-message mark.
194 ssl_crt_validator->eom = '\1';
aee3523a 195 assert(ssl_crt_validator->cmdline == nullptr);
14798e73 196
72247610
AJ
197 /* defaults */
198 int ttl = 3600; // 1 hour
199 size_t cache = 64*1024*1024; // 64 MB
2cef0ca6 200 {
72247610 201 // TODO: Do this during parseConfigFile() for proper parsing, error handling
2cef0ca6
AR
202 char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
203 char *tmp_begin = tmp;
aee3523a 204 char * token = nullptr;
14798e73 205 bool parseParams = true;
aee3523a 206 while ((token = strwordtok(nullptr, &tmp))) {
14798e73 207 if (parseParams) {
72247610
AJ
208 if (strcmp(token, "ttl=infinity") == 0) {
209 ttl = std::numeric_limits<CacheType::Ttl>::max();
210 continue;
211 } else if (strncmp(token, "ttl=", 4) == 0) {
212 ttl = xatoi(token + 4);
213 if (ttl < 0) {
214 throw TextException(ToSBuf("Negative TTL in sslcrtvalidator_program ", Ssl::TheConfig.ssl_crt_validator,
70ac5b29 215 Debug::Extra, "For unlimited TTL, use ttl=infinity"),
216 Here());
72247610 217 }
14798e73
CT
218 continue;
219 } else if (strncmp(token, "cache=", 6) == 0) {
72247610 220 cache = xatoi(token + 6);
14798e73
CT
221 continue;
222 } else
223 parseParams = false;
224 }
2cef0ca6
AR
225 wordlistAdd(&ssl_crt_validator->cmdline, token);
226 }
4a77bb4e 227 xfree(tmp_begin);
2cef0ca6 228 }
bd71920d 229 ssl_crt_validator->openSessions();
14798e73
CT
230
231 //WARNING: initializing static member in an object initialization method
aee3523a 232 assert(HelperCache == nullptr);
72247610 233 HelperCache = new CacheType(cache, ttl);
2cef0ca6
AR
234}
235
236void Ssl::CertValidationHelper::Shutdown()
237{
238 if (!ssl_crt_validator)
239 return;
240 helperShutdown(ssl_crt_validator);
241 wordlistDestroy(&ssl_crt_validator->cmdline);
aee3523a 242 ssl_crt_validator = nullptr;
4c304fb9 243
14798e73 244 // CertValidationHelper::HelperCache is a static member, it is not good policy to
4c304fb9 245 // reset it here. Will work because the current Ssl::CertValidationHelper is
14798e73
CT
246 // always the same static object.
247 delete HelperCache;
aee3523a 248 HelperCache = nullptr;
14798e73
CT
249}
250
23da195f
CT
251void
252Ssl::CertValidationHelper::Reconfigure()
253{
254 Shutdown();
255 Init();
256}
257
5c2f68b7
AJ
258class submitData
259{
260 CBDATA_CLASS(submitData);
261
262public:
5107d2c4 263 SBuf query;
e5ddd4ce 264 Ssl::CertValidationHelper::Callback callback;
e601ca5d 265 Security::SessionPointer ssl;
14798e73
CT
266};
267CBDATA_CLASS_INIT(submitData);
268
269static void
24438ec5 270sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
14798e73
CT
271{
272 Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
14798e73
CT
273
274 submitData *crtdvdData = static_cast<submitData *>(data);
8fe1a85a
CT
275 assert(crtdvdData->ssl.get());
276 Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse(crtdvdData->ssl);
2428ce02 277 if (reply.result == ::Helper::BrokenHelper) {
d816f28d 278 debugs(83, DBG_IMPORTANT, "ERROR: \"ssl_crtvd\" helper error response: " << reply.other().content());
2428ce02 279 validationResponse->resultCode = ::Helper::BrokenHelper;
ddc77a2e
CT
280 } else if (!reply.other().hasContent()) {
281 debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response");
282 validationResponse->resultCode = ::Helper::BrokenHelper;
14798e73 283 } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
8b082ed9 284 !replyMsg.parseResponse(*validationResponse) ) {
14798e73 285 debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
d816f28d 286 debugs(83, DBG_IMPORTANT, "ERROR: Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
2428ce02 287 validationResponse->resultCode = ::Helper::BrokenHelper;
4c304fb9 288 } else
14798e73
CT
289 validationResponse->resultCode = reply.result;
290
e5ddd4ce
AR
291 crtdvdData->callback.answer() = validationResponse;
292 ScheduleCallHere(crtdvdData->callback.release());
14798e73
CT
293
294 if (Ssl::CertValidationHelper::HelperCache &&
2428ce02 295 (validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
72247610 296 (void)Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, validationResponse);
0e208dad 297 }
14798e73 298
14798e73 299 delete crtdvdData;
2cef0ca6
AR
300}
301
e5ddd4ce
AR
302void
303Ssl::CertValidationHelper::Submit(const Ssl::CertValidationRequest &request, const Callback &callback)
2cef0ca6 304{
14798e73
CT
305 Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
306 message.setCode(Ssl::CertValidationMsg::code_cert_validate);
307 message.composeRequest(request);
308 debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
309
310 submitData *crtdvdData = new submitData;
5107d2c4
CT
311 crtdvdData->query.assign(message.compose().c_str());
312 crtdvdData->query.append('\n');
14798e73 313 crtdvdData->callback = callback;
0b168d25 314 crtdvdData->ssl = request.ssl;
0e208dad 315 Ssl::CertValidationResponse::Pointer const*validationResponse;
14798e73
CT
316
317 if (CertValidationHelper::HelperCache &&
5107d2c4 318 (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query))) {
0e208dad 319
e5ddd4ce
AR
320 crtdvdData->callback.answer() = *validationResponse;
321 ScheduleCallHere(crtdvdData->callback.release());
14798e73
CT
322 delete crtdvdData;
323 return;
324 }
6825b101 325
23da195f
CT
326 // ssl_crt_validator becomes nil if Squid is reconfigured with cert
327 // validator disabled in the new configuration
328 if (ssl_crt_validator && ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData))
6825b101 329 return;
23da195f
CT
330
331 Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse(crtdvdData->ssl);
332 resp->resultCode = ::Helper::BrokenHelper;
e5ddd4ce
AR
333 crtdvdData->callback.answer() = resp;
334 ScheduleCallHere(crtdvdData->callback.release());
23da195f
CT
335 delete crtdvdData;
336 return;
2cef0ca6 337}
f53969cc 338