helper.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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 "base/AsyncCallbacks.h"
13 #include "cache_cf.h"
14 #include "fs_io.h"
15 #include "helper/Reply.h"
16 #include "Parsing.h"
17 #include "sbuf/Stream.h"
18 #include "SquidConfig.h"
20 #include "ssl/Config.h"
21 #include "ssl/helper.h"
22 #include "wordlist.h"
23 
24 #include <limits>
25 
27 
28 #if USE_SSL_CRTD
29 
30 namespace Ssl {
31 
34 public:
35  GeneratorRequestor(HLPCB *aCallback, void *aData): callback(aCallback), data(aData) {}
38 };
39 
43 
44 public:
46  void emplace(HLPCB *callback, void *data) { requestors.emplace_back(callback, data); }
47 
49 
51  typedef std::vector<GeneratorRequestor> GeneratorRequestors;
53 };
54 
56 typedef std::unordered_map<SBuf, GeneratorRequest*> GeneratorRequests;
57 
58 static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply);
59 
61 static GeneratorRequests &
63 {
64  static auto generatorRequests = new GeneratorRequests();
65  return *generatorRequests;
66 }
67 
68 } // namespace Ssl
69 
70 CBDATA_NAMESPACED_CLASS_INIT(Ssl, GeneratorRequest);
71 
73 static std::ostream &
74 operator <<(std::ostream &os, const Ssl::GeneratorRequest &gr)
75 {
76  return os << "crtGenRq" << gr.query.id.value << "/" << gr.requestors.size();
77 }
78 
80 
82 {
83  assert(ssl_crtd == nullptr);
84 
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
87  bool found = false;
88  for (AnyP::PortCfgPointer s = HttpPortList; !found && s != nullptr; s = s->next)
89  found = s->flags.tunnelSslBumping && s->secure.generateHostCertificates;
90  if (!found)
91  return;
92 
93  ssl_crtd = ::Helper::Client::Make("sslcrtd_program");
94  ssl_crtd->childs.updateLimits(Ssl::TheConfig.ssl_crtdChildren);
95  ssl_crtd->ipc_type = IPC_STREAM;
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';
99  assert(ssl_crtd->cmdline == nullptr);
100  {
101  char *tmp = xstrdup(Ssl::TheConfig.ssl_crtd);
102  char *tmp_begin = tmp;
103  char *token = nullptr;
104  while ((token = strwordtok(nullptr, &tmp))) {
105  wordlistAdd(&ssl_crtd->cmdline, token);
106  }
107  safe_free(tmp_begin);
108  }
109  ssl_crtd->openSessions();
110 }
111 
113 {
114  if (!ssl_crtd)
115  return;
116  helperShutdown(ssl_crtd);
117  wordlistDestroy(&ssl_crtd->cmdline);
118  ssl_crtd = nullptr;
119 }
120 
121 void
123 {
124  Shutdown();
125  Init();
126 }
127 
128 void Ssl::Helper::Submit(CrtdMessage const & message, HLPCB * callback, void * data)
129 {
130  SBuf rawMessage(message.compose().c_str()); // XXX: helpers cannot use SBuf
131  rawMessage.append("\n", 1);
132 
133  const auto pending = TheGeneratorRequests().find(rawMessage);
134  if (pending != TheGeneratorRequests().end()) {
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);
143  TheGeneratorRequests().emplace(request->query, request);
144  debugs(83, 5, "request from " << data << " as " << *request);
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))
148  return;
149 
151  failReply.notes.add("message", "error 45 Temporary network problem, please retry later");
152  HandleGeneratorReply(request, failReply);
153 }
154 
156 static void
157 Ssl::HandleGeneratorReply(void *data, const ::Helper::Reply &reply)
158 {
159  const std::unique_ptr<Ssl::GeneratorRequest> request(static_cast<Ssl::GeneratorRequest*>(data));
160  assert(request);
161  const auto erased = TheGeneratorRequests().erase(request->query);
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  }
169  }
170 }
171 #endif //USE_SSL_CRTD
172 
174 
176 {
177  if (!Ssl::TheConfig.ssl_crt_validator)
178  return;
179 
180  assert(ssl_crt_validator == nullptr);
181 
182  // we need to start ssl_crtd only if some port(s) need to bump SSL
183  bool found = false;
184  for (AnyP::PortCfgPointer s = HttpPortList; !found && s != nullptr; s = s->next)
185  found = s->flags.tunnelSslBumping;
186  if (!found)
187  return;
188 
189  ssl_crt_validator = ::Helper::Client::Make("ssl_crt_validator");
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';
195  assert(ssl_crt_validator->cmdline == nullptr);
196 
197  /* defaults */
198  int ttl = 3600; // 1 hour
199  size_t cache = 64*1024*1024; // 64 MB
200  {
201  // TODO: Do this during parseConfigFile() for proper parsing, error handling
202  char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
203  char *tmp_begin = tmp;
204  char * token = nullptr;
205  bool parseParams = true;
206  while ((token = strwordtok(nullptr, &tmp))) {
207  if (parseParams) {
208  if (strcmp(token, "ttl=infinity") == 0) {
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,
215  Debug::Extra, "For unlimited TTL, use ttl=infinity"),
216  Here());
217  }
218  continue;
219  } else if (strncmp(token, "cache=", 6) == 0) {
220  cache = xatoi(token + 6);
221  continue;
222  } else
223  parseParams = false;
224  }
225  wordlistAdd(&ssl_crt_validator->cmdline, token);
226  }
227  xfree(tmp_begin);
228  }
229  ssl_crt_validator->openSessions();
230 
231  //WARNING: initializing static member in an object initialization method
232  assert(HelperCache == nullptr);
233  HelperCache = new CacheType(cache, ttl);
234 }
235 
237 {
238  if (!ssl_crt_validator)
239  return;
240  helperShutdown(ssl_crt_validator);
241  wordlistDestroy(&ssl_crt_validator->cmdline);
242  ssl_crt_validator = nullptr;
243 
244  // CertValidationHelper::HelperCache is a static member, it is not good policy to
245  // reset it here. Will work because the current Ssl::CertValidationHelper is
246  // always the same static object.
247  delete HelperCache;
248  HelperCache = nullptr;
249 }
250 
251 void
253 {
254  Shutdown();
255  Init();
256 }
257 
259 {
261 
262 public:
263  SBuf query;
266 };
268 
269 static void
270 sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
271 {
273 
274  submitData *crtdvdData = static_cast<submitData *>(data);
275  assert(crtdvdData->ssl.get());
276  Ssl::CertValidationResponse::Pointer validationResponse = new Ssl::CertValidationResponse(crtdvdData->ssl);
277  if (reply.result == ::Helper::BrokenHelper) {
278  debugs(83, DBG_IMPORTANT, "ERROR: \"ssl_crtvd\" helper error response: " << reply.other().content());
279  validationResponse->resultCode = ::Helper::BrokenHelper;
280  } else if (!reply.other().hasContent()) {
281  debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper returned NULL response");
282  validationResponse->resultCode = ::Helper::BrokenHelper;
283  } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
284  !replyMsg.parseResponse(*validationResponse) ) {
285  debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
286  debugs(83, DBG_IMPORTANT, "ERROR: Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
287  validationResponse->resultCode = ::Helper::BrokenHelper;
288  } else
289  validationResponse->resultCode = reply.result;
290 
291  crtdvdData->callback.answer() = validationResponse;
292  ScheduleCallHere(crtdvdData->callback.release());
293 
295  (validationResponse->resultCode == ::Helper::Okay || validationResponse->resultCode == ::Helper::Error)) {
296  (void)Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query, validationResponse);
297  }
298 
299  delete crtdvdData;
300 }
301 
302 void
304 {
307  message.composeRequest(request);
308  debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
309 
310  submitData *crtdvdData = new submitData;
311  crtdvdData->query.assign(message.compose().c_str());
312  crtdvdData->query.append('\n');
313  crtdvdData->callback = callback;
314  crtdvdData->ssl = request.ssl;
315  Ssl::CertValidationResponse::Pointer const*validationResponse;
316 
318  (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query))) {
319 
320  crtdvdData->callback.answer() = *validationResponse;
321  ScheduleCallHere(crtdvdData->callback.release());
322  delete crtdvdData;
323  return;
324  }
325 
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))
329  return;
330 
332  resp->resultCode = ::Helper::BrokenHelper;
333  crtdvdData->callback.answer() = resp;
334  ScheduleCallHere(crtdvdData->callback.release());
335  delete crtdvdData;
336  return;
337 }
338 
an old-style void* callback parameter
Definition: cbdata.h:383
AsyncCallback< Answer > Callback
Definition: helper.h:47
Value value
instance identifier
Definition: InstanceId.h:70
std::vector< GeneratorRequestor > GeneratorRequestors
Ssl::Helper request initiators waiting for the same answer (FIFO).
Definition: helper.cc:51
#define Here()
source code location of the caller
Definition: Here.h:15
void wordlistDestroy(wordlist **list)
destroy a wordlist
Definition: wordlist.cc:16
Security::SessionPointer ssl
static void HandleGeneratorReply(void *data, const ::Helper::Reply &reply)
receives helper response
Definition: helper.cc:157
SBuf & assign(const SBuf &S)
Definition: SBuf.cc:83
@ Error
Definition: ResultCode.h:19
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
Security::SessionPointer ssl
Definition: helper.cc:265
#define CBDATA_NAMESPACED_CLASS_INIT(namespace, type)
Definition: cbdata.h:333
static const std::string code_cert_validate
String code for "cert_validate" messages.
Initiator of an Ssl::Helper query.
Definition: helper.cc:33
void composeRequest(CertValidationRequest const &vcert)
GeneratorRequestors requestors
Definition: helper.cc:52
#define ScheduleCallHere(call)
Definition: AsyncCall.h:166
static void Submit(CrtdMessage const &message, HLPCB *callback, void *data)
Submit crtd message to external crtd server.
Definition: helper.cc:128
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
Definition: SBuf.h:93
static void sslCrtvdHandleReplyWrapper(void *data, const ::Helper::Reply &reply)
Definition: helper.cc:270
static void Shutdown()
Shutdown helper structure.
Definition: helper.cc:236
#define xstrdup
static void Submit(const Ssl::CertValidationRequest &, const Callback &)
Submit crtd request message to external crtd server.
Definition: helper.cc:303
const A & max(A const &lhs, A const &rhs)
Definition: cbdata.cc:37
static Pointer Make(const char *name)
Definition: helper.cc:757
SBuf query
Ssl::Helper request message (GeneratorRequests key)
Definition: helper.cc:48
void helperShutdown(const Helper::Client::Pointer &hlp)
Definition: helper.cc:769
static void Reconfigure()
Definition: helper.cc:252
static void Init()
Init helper structure.
Definition: helper.cc:81
static GeneratorRequests & TheGeneratorRequests()
pending Ssl::Helper requests (to all certificate generator helpers combined)
Definition: helper.cc:62
static void Init()
Init helper structure.
Definition: helper.cc:175
void * data
Definition: cbdata.cc:78
static void Shutdown()
Shutdown helper structure.
Definition: helper.cc:112
std::string compose() const
char * strwordtok(char *buf, char **t)
Definition: String.cc:314
Definition: ClpMap.h:40
Definition: Xaction.cc:39
NotePairs notes
Definition: Reply.h:62
::Helper::ClientPointer ssl_crt_validator
helper for management of ssl_crtd.
Definition: helper.h:56
int xatoi(const char *token)
Definition: Parsing.cc:44
#define safe_free(x)
Definition: xalloc.h:73
static std::ostream & operator<<(std::ostream &os, const Ssl::GeneratorRequest &gr)
prints Ssl::GeneratorRequest for debugging
Definition: helper.cc:74
Config TheConfig
Definition: Config.cc:12
#define assert(EX)
Definition: assert.h:17
@ Okay
Definition: ResultCode.h:18
static void Reconfigure()
Definition: helper.cc:122
const char * c_str()
Definition: SBuf.cc:516
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
::Helper::ClientPointer ssl_crtd
helper for management of ssl_crtd.
Definition: helper.h:37
static std::ostream & Extra(std::ostream &)
Definition: debug.cc:1316
SBuf & append(const SBuf &S)
Definition: SBuf.cc:185
#define xfree
void setCode(std::string const &aCode)
Set new request/reply code to compose.
bool parseResponse(CertValidationResponse &resp)
Parse a response message and fill the resp object with parsed information.
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:53
ParseResult parse(const char *buffer, size_t len)
Definition: crtd_message.cc:23
CallbackData data
Definition: helper.cc:37
SBuf query
Definition: helper.cc:260
an std::runtime_error with thrower location info
Definition: TextException.h:20
SBuf ToSBuf(Args &&... args)
slowly stream-prints all arguments into a freshly allocated SBuf
Definition: Stream.h:63
A pending Ssl::Helper request, combining the original and collapsed queries.
Definition: helper.cc:41
#define DBG_IMPORTANT
Definition: Stream.h:38
Ssl::CertValidationHelper::Callback callback
Definition: helper.cc:264
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:317
@ BrokenHelper
Definition: ResultCode.h:20
GeneratorRequestor(HLPCB *aCallback, void *aData)
Definition: helper.cc:35
std::unordered_map< SBuf, GeneratorRequest * > GeneratorRequests
Ssl::Helper query:GeneratorRequest map.
Definition: helper.cc:56
void HLPCB(void *, const Helper::Reply &)
Definition: forward.h:33
static CacheType * HelperCache
cache for cert validation helper
Definition: helper.h:59
const char * wordlistAdd(wordlist **list, const char *key)
Definition: wordlist.cc:25
const InstanceId< SBuf > id
Definition: SBuf.h:608
#define IPC_STREAM
Definition: defines.h:104
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void Init(void)
prepares to parse ACLs configuration
Definition: AclRegs.cc:189
void emplace(HLPCB *callback, void *data)
adds a GeneratorRequestor
Definition: helper.cc:46
const std::string & getBody() const
Current body. If parsing is not finished the method returns incompleted body.

 

Introduction

Documentation

Support

Miscellaneous