Session.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 /* DEBUG: section 83 TLS session management */
10 
11 #include "squid.h"
12 #include "anyp/PortCfg.h"
13 #include "base/RunnersRegistry.h"
14 #include "CachePeer.h"
15 #include "debug/Stream.h"
16 #include "fd.h"
17 #include "fde.h"
18 #include "ipc/MemMap.h"
19 #include "security/Session.h"
20 #include "SquidConfig.h"
21 #include "ssl/bio.h"
22 
23 #define SSL_SESSION_ID_SIZE 32
24 #define SSL_SESSION_MAX_SIZE 10*1024
25 
26 #if USE_OPENSSL
27 static Ipc::MemMap *SessionCache = nullptr;
28 static const char *SessionCacheName = "tls_session_cache";
29 #endif
30 
31 #if USE_OPENSSL || USE_GNUTLS
32 static int
33 tls_read_method(int fd, char *buf, int len)
34 {
35  auto session = fd_table[fd].ssl.get();
36  debugs(83, 3, "started for session=" << (void*)session);
37 
38 #if USE_OPENSSL
39  int i = SSL_read(session, buf, len);
40 #elif USE_GNUTLS
41  int i = gnutls_record_recv(session, buf, len);
42 #endif
43 
44  if (i > 0) {
45  debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
46  (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
47  }
48 
49 #if USE_OPENSSL
50  if (i > 0 && SSL_pending(session) > 0) {
51 #elif USE_GNUTLS
52  if (i > 0 && gnutls_record_check_pending(session) > 0) {
53 #endif
54  debugs(83, 2, "TLS FD " << fd << " is pending");
55  fd_table[fd].flags.read_pending = true;
56  } else
57  fd_table[fd].flags.read_pending = false;
58 
59  return i;
60 }
61 
62 static int
63 tls_write_method(int fd, const char *buf, int len)
64 {
65  auto session = fd_table[fd].ssl.get();
66  debugs(83, 3, "started for session=" << (void*)session);
67 
68 #if USE_OPENSSL
69  if (!SSL_is_init_finished(session)) {
70  errno = ENOTCONN;
71  return -1;
72  }
73 #endif
74 
75 #if USE_OPENSSL
76  int i = SSL_write(session, buf, len);
77 #elif USE_GNUTLS
78  int i = gnutls_record_send(session, buf, len);
79 #endif
80 
81  if (i > 0) {
82  debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
83  }
84  return i;
85 }
86 #endif
87 
88 #if USE_OPENSSL
91 {
92  Security::SessionPointer session(SSL_new(ctx.get()), [](SSL *p) {
93  debugs(83, 5, "SSL_free session=" << (void*)p);
94  SSL_free(p);
95  });
96  debugs(83, 5, "SSL_new session=" << (void*)session.get());
97  return session;
98 }
99 #endif
100 
101 static bool
103 {
104  if (!Comm::IsConnOpen(conn)) {
105  debugs(83, DBG_IMPORTANT, "Gone connection");
106  return false;
107  }
108 
109 #if USE_OPENSSL || USE_GNUTLS
110 
111  const char *errAction = "with no TLS/SSL library";
112  Security::LibErrorCode errCode = 0;
113 #if USE_OPENSSL
115  if (!session) {
116  errCode = ERR_get_error();
117  errAction = "failed to allocate handle";
118  debugs(83, DBG_IMPORTANT, "ERROR: TLS failure: " << errAction << ": " << Security::ErrorString(errCode));
119  }
120 #elif USE_GNUTLS
121  gnutls_session_t tmp;
122  errCode = gnutls_init(&tmp, static_cast<unsigned int>(type) | GNUTLS_NONBLOCK);
123  Security::SessionPointer session(tmp, [](gnutls_session_t p) {
124  debugs(83, 5, "gnutls_deinit session=" << (void*)p);
125  gnutls_deinit(p);
126  });
127  debugs(83, 5, "gnutls_init " << (type == Security::Io::BIO_TO_SERVER ? "client" : "server" )<< " session=" << (void*)session.get());
128  if (errCode != GNUTLS_E_SUCCESS) {
129  session.reset();
130  errAction = "failed to initialize session";
131  debugs(83, DBG_IMPORTANT, "ERROR: TLS failure: " << errAction << ": " << Security::ErrorString(errCode));
132  }
133 #endif /* USE_GNUTLS */
134 
135  if (session) {
136  const int fd = conn->fd;
137 
138 #if USE_OPENSSL
139  // without BIO, we would call SSL_set_fd(ssl.get(), fd) instead
140  if (BIO *bio = Ssl::Bio::Create(fd, type)) {
141  Ssl::Bio::Link(session.get(), bio); // cannot fail
142 #elif USE_GNUTLS
143  errCode = gnutls_credentials_set(session.get(), GNUTLS_CRD_CERTIFICATE, ctx.get());
144  if (errCode == GNUTLS_E_SUCCESS) {
145 
146  opts.updateSessionOptions(session);
147 
148  // NP: GnuTLS does not yet support the BIO operations
149  // this does the equivalent of SSL_set_fd() for now.
150  gnutls_transport_set_int(session.get(), fd);
151  gnutls_handshake_set_timeout(session.get(), GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
152 #endif /* USE_GNUTLS */
153 
154  debugs(83, 5, "link FD " << fd << " to TLS session=" << (void*)session.get());
155 
156  fd_table[fd].ssl = session;
157  fd_table[fd].useBufferedIo(&tls_read_method, &tls_write_method);
158  fd_note(fd, squidCtx);
159  return true;
160  }
161 
162 #if USE_OPENSSL
163  errCode = ERR_get_error();
164  errAction = "failed to initialize I/O";
165  (void)opts;
166 #elif USE_GNUTLS
167  errAction = "failed to assign credentials";
168 #endif
169  }
170 
171  debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
172  ": " << (errCode != 0 ? Security::ErrorString(errCode) : ""));
173 #else
174  (void)ctx;
175  (void)opts;
176  (void)type;
177  (void)squidCtx;
178 #endif /* USE_OPENSSL || USE_GNUTLS */
179  return false;
180 }
181 
182 bool
184 {
185  if (!c || !c->getPeer())
187 
188  auto *peer = c->getPeer();
189  return CreateSession(ctx, c, peer->secure, Security::Io::BIO_TO_SERVER, squidCtx);
190 }
191 
192 bool
194 {
195  return CreateSession(ctx, c, o, Security::Io::BIO_TO_CLIENT, squidCtx);
196 }
197 
198 void
200 {
201  debugs(83, 5, "session=" << (void*)s.get());
202  if (s) {
203 #if USE_OPENSSL
204  SSL_shutdown(s.get());
205 #elif USE_GNUTLS
206  gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
207 #endif
208  }
209 }
210 
211 bool
213 {
214  bool result = false;
215 #if USE_OPENSSL
216  result = SSL_session_reused(s.get()) == 1;
217 #elif USE_GNUTLS
218  result = gnutls_session_is_resumed(s.get()) != 0;
219 #endif
220  debugs(83, 7, "session=" << (void*)s.get() << ", query? answer: " << (result ? 'T' : 'F') );
221  return result;
222 }
223 
224 void
226 {
227  if (!SessionIsResumed(s)) {
228 #if USE_OPENSSL
229  // nil is valid for SSL_get1_session(), it cannot fail.
230  data.reset(SSL_get1_session(s.get()));
231 #elif USE_GNUTLS
232  gnutls_datum_t *tmp = nullptr;
233  const auto x = gnutls_session_get_data2(s.get(), tmp);
234  if (x != GNUTLS_E_SUCCESS) {
235  debugs(83, 3, "session=" << (void*)s.get() << " error: " << Security::ErrorString(x));
236  }
237  data.reset(tmp);
238 #endif
239  debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get());
240  } else {
241  debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get() << ", do nothing.");
242  }
243 }
244 
245 void
247 {
248  if (data) {
249 #if USE_OPENSSL
250  if (!SSL_set_session(s.get(), data.get())) {
251  const auto ssl_error = ERR_get_error();
252  debugs(83, 3, "session=" << (void*)s.get() << " data=" << (void*)data.get() <<
253  " resume error: " << Security::ErrorString(ssl_error));
254  }
255 #elif USE_GNUTLS
256  const auto x = gnutls_session_set_data(s.get(), data->data, data->size);
257  if (x != GNUTLS_E_SUCCESS) {
258  debugs(83, 3, "session=" << (void*)s.get() << " data=" << (void*)data.get() <<
259  " resume error: " << Security::ErrorString(x));
260  }
261 #else
262  // critical because, how did it get here?
263  debugs(83, DBG_CRITICAL, "no TLS library. session=" << (void*)s.get() << " data=" << (void*)data.get());
264 #endif
265  debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get());
266  } else {
267  debugs(83, 5, "session=" << (void*)s.get() << " no resume data");
268  }
269 }
270 
271 static bool
273 {
274  for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
275  if (s->secure.encryptTransport)
276  return true;
277  if (s->flags.tunnelSslBumping)
278  return true;
279  }
280 
281  return false;
282 }
283 
284 #if USE_OPENSSL
285 static int
286 store_session_cb(SSL *, SSL_SESSION *session)
287 {
288  if (!SessionCache)
289  return 0;
290 
291  debugs(83, 5, "Request to store SSL_SESSION");
292 
293  SSL_SESSION_set_timeout(session, Config.SSL.session_ttl);
294 
295  unsigned int idlen;
296  const unsigned char *id = SSL_SESSION_get_id(session, &idlen);
297  // XXX: the other calls [to openForReading()] do not copy the sessionId to a char buffer, does this really have to?
298  unsigned char key[MEMMAP_SLOT_KEY_SIZE];
299  // Session ids are of size 32bytes. They should always fit to a
300  // MemMap::Slot::key
301  assert(idlen <= MEMMAP_SLOT_KEY_SIZE);
302  memset(key, 0, sizeof(key));
303  memcpy(key, id, idlen);
304  int pos;
305  if (auto slotW = SessionCache->openForWriting(static_cast<const cache_key*>(key), pos)) {
306  int lenRequired = i2d_SSL_SESSION(session, nullptr);
307  if (lenRequired < MEMMAP_SLOT_DATA_SIZE) {
308  unsigned char *p = static_cast<unsigned char *>(slotW->p);
309  lenRequired = i2d_SSL_SESSION(session, &p);
310  slotW->set(key, nullptr, lenRequired, squid_curtime + Config.SSL.session_ttl);
311  }
313  debugs(83, 5, "wrote an SSL_SESSION entry of size " << lenRequired << " at pos " << pos);
314  }
315  return 0;
316 }
317 
318 static void
319 remove_session_cb(SSL_CTX *, SSL_SESSION *sessionID)
320 {
321  if (!SessionCache)
322  return;
323 
324  debugs(83, 5, "Request to remove corrupted or not valid SSL_SESSION");
325  int pos;
326  if (SessionCache->openForReading(reinterpret_cast<const cache_key*>(sessionID), pos)) {
328  // TODO:
329  // What if we are not able to remove the session?
330  // Maybe schedule a job to remove it later?
331  // For now we just have an invalid entry in cache until will be expired
332  // The OpenSSL library will reject it when we try to use it
333  SessionCache->free(pos);
334  }
335 }
336 
337 static SSL_SESSION *
338 #if SQUID_USE_CONST_SSL_SESSION_CBID
339 get_session_cb(SSL *, const unsigned char *sessionID, int len, int *copy)
340 #else
341 get_session_cb(SSL *, unsigned char *sessionID, int len, int *copy)
342 #endif
343 {
344  if (!SessionCache)
345  return nullptr;
346 
347  const unsigned int *p = reinterpret_cast<const unsigned int *>(sessionID);
348  debugs(83, 5, "Request to search for SSL_SESSION of len: " <<
349  len << p[0] << ":" << p[1]);
350 
351  SSL_SESSION *session = nullptr;
352  int pos;
353  if (const auto slot = SessionCache->openForReading(static_cast<const cache_key*>(sessionID), pos)) {
354  if (slot->expire > squid_curtime) {
355  const unsigned char *ptr = slot->p;
356  session = d2i_SSL_SESSION(nullptr, &ptr, slot->pSize);
357  debugs(83, 5, "SSL_SESSION retrieved from cache at pos " << pos);
358  } else
359  debugs(83, 5, "SSL_SESSION in cache expired");
361  }
362 
363  if (!session)
364  debugs(83, 5, "Failed to retrieve SSL_SESSION from cache");
365 
366  // With the parameter copy the callback can require the SSL engine
367  // to increment the reference count of the SSL_SESSION object, Normally
368  // the reference count is not incremented and therefore the session must
369  // not be explicitly freed with SSL_SESSION_free(3).
370  *copy = 0;
371  return session;
372 }
373 
374 void
376 {
377  if (SessionCache) {
378  SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL);
379  SSL_CTX_sess_set_new_cb(ctx.get(), store_session_cb);
380  SSL_CTX_sess_set_remove_cb(ctx.get(), remove_session_cb);
381  SSL_CTX_sess_set_get_cb(ctx.get(), get_session_cb);
382  }
383 }
384 #endif /* USE_OPENSSL */
385 
386 static void
388 {
389 #if USE_OPENSSL
390  // Check if the MemMap keys and data are enough big to hold
391  // session ids and session data
394 
395  int configuredItems = ::Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
396  if (IamWorkerProcess() && configuredItems)
398  else {
399  SessionCache = nullptr;
400  return;
401  }
402 
403  for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
404  if (s->secure.staticContext)
405  Security::SetSessionCacheCallbacks(s->secure.staticContext);
406  }
407 #endif
408 }
409 
412 {
413 public:
414  /* RegisteredRunner API */
416  virtual void useConfig();
417  virtual ~SharedSessionCacheRr();
418 
419 protected:
420  virtual void create();
421 
422 private:
424 };
425 
427 
428 void
430 {
431 #if USE_OPENSSL
432  if (SessionCache || !isTlsServer()) // no need to configure SSL_SESSION* cache.
433  return;
434 
437 #endif
438 }
439 
440 void
442 {
443  if (!isTlsServer()) // no need to configure SSL_SESSION* cache.
444  return;
445 
446 #if USE_OPENSSL
447  if (int items = Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot))
449 #endif
450 }
451 
453 {
454  // XXX: Enable after testing to reduce at-exit memory "leaks".
455  // delete SessionCache;
456 
457  delete owner;
458 }
459 
bool CreateClientSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx)
Definition: Session.cc:183
static void Link(SSL *ssl, BIO *bio)
Tells ssl connection to use BIO and monitor state via stateChanged()
Definition: bio.cc:88
#define VALGRIND_MAKE_MEM_DEFINED
Definition: valgrind.h:27
#define DBG_CRITICAL
Definition: Stream.h:40
static bool CreateSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &conn, Security::PeerOptions &opts, Security::Io::Type type, const char *squidCtx)
Definition: Session.cc:102
static Ipc::MemMap * SessionCache
Definition: Session.cc:27
std::shared_ptr< SSL_CTX > ContextPointer
Definition: Context.h:29
virtual ~SharedSessionCacheRr()
Definition: Session.cc:452
a MemMap basic element, holding basic shareable memory block info
Definition: MemMap.h:34
void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &data)
Definition: Session.cc:225
unsigned char cache_key
Store key.
Definition: forward.h:29
AnyP::PortCfgPointer HttpPortList
list of Squid http(s)_port configured
Definition: PortCfg.cc:22
void fd_note(int fd, const char *s)
Definition: fd.cc:216
static int tls_read_method(int fd, char *buf, int len)
Definition: Session.cc:33
static void initializeSessionCache()
Definition: Session.cc:387
A map of MemMapSlots indexed by their keys, with read/write slot locking.
Definition: MemMap.h:57
const Slot * openForReading(const cache_key *const key, sfileno &fileno)
open slot for reading, increments read level
Definition: MemMap.cc:153
#define SSL_SESSION_ID_SIZE
Definition: Session.cc:23
static int tls_write_method(int fd, const char *buf, int len)
Definition: Session.cc:63
virtual void useConfig()
Definition: Session.cc:429
#define SSL_SESSION_MAX_SIZE
Definition: Session.cc:24
void closeForWriting(const sfileno fileno)
successfully finish writing the entry
Definition: MemMap.cc:91
int type
Definition: errorpage.cc:152
size_t sessionCacheSize
Definition: SquidConfig.h:506
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:27
bool IamWorkerProcess()
whether the current process handles HTTP transactions and such
Definition: stub_tools.cc:47
initializes shared memory segments used by MemStore
Definition: Session.cc:412
PeerOptions ProxyOutgoingConfig
configuration options for DIRECT server access
Definition: PeerOptions.cc:24
@ BIO_TO_SERVER
Definition: forward.h:143
static BIO * Create(const int fd, Security::Io::Type type)
Definition: bio.cc:62
#define MEMMAP_SLOT_KEY_SIZE
Definition: MemMap.h:29
void SessionSendGoodbye(const Security::SessionPointer &)
send the shutdown/bye notice for an active TLS session.
Definition: Session.cc:199
bool SessionIsResumed(const Security::SessionPointer &)
whether the session is a resumed one
Definition: Session.cc:212
struct SquidConfig::@119 SSL
@ BIO_TO_CLIENT
Definition: forward.h:142
static int store_session_cb(SSL *, SSL_SESSION *session)
Definition: Session.cc:286
TLS squid.conf settings for a remote server peer.
Definition: PeerOptions.h:24
#define MEMMAP_SLOT_DATA_SIZE
Definition: MemMap.h:30
CachePeer * getPeer() const
Definition: Connection.cc:124
void SetSessionCacheCallbacks(Security::ContextPointer &)
Setup the given TLS context with callbacks used to manage the session cache.
Definition: Session.cc:375
static Owner * Init(const char *const path, const int limit)
initialize shared memory
Definition: MemMap.cc:36
Security::SessionPointer NewSessionObject(const Security::ContextPointer &)
Definition: Session.cc:90
static void remove_session_cb(SSL_CTX *, SSL_SESSION *sessionID)
Definition: Session.cc:319
int conn
the current server connection FD
Definition: Transport.cc:26
MemMapSlot Slot
Definition: MemMap.h:59
#define assert(EX)
Definition: assert.h:19
static const char * SessionCacheName
Definition: Session.cc:28
time_t squid_curtime
Definition: stub_libtime.cc:20
bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, Security::PeerOptions &, const char *squidCtx)
Definition: Session.cc:193
static SSL_SESSION * get_session_cb(SSL *, unsigned char *sessionID, int len, int *copy)
Definition: Session.cc:341
Ipc::MemMap::Owner * owner
Definition: Session.cc:423
const unsigned char * SSL_SESSION_get_id(const SSL_SESSION *s, unsigned int *len)
Definition: openssl.h:147
virtual void create()
called when the runner should create a new memory segment
Definition: Session.cc:441
#define fd_table
Definition: fde.h:189
std::shared_ptr< SSL > SessionPointer
Definition: Session.h:49
Slot * openForWriting(const cache_key *const key, sfileno &fileno)
Definition: MemMap.cc:42
std::unique_ptr< SSL_SESSION, HardFun< void, SSL_SESSION *, &SSL_SESSION_free > > SessionStatePointer
Definition: Session.h:51
void updateSessionOptions(Security::SessionPointer &)
setup any library-specific options that can be set for the given session
Definition: PeerOptions.cc:770
#define DBG_IMPORTANT
Definition: Stream.h:41
RunnerRegistrationEntry(SharedSessionCacheRr)
void free(const sfileno fileno)
mark the slot as waiting to be freed and, if possible, free it
Definition: MemMap.cc:138
virtual void useConfig()
Definition: Segment.cc:377
static bool isTlsServer()
Definition: Session.cc:272
const char * ErrorString(const LibErrorCode code)
converts numeric LibErrorCode into a human-friendlier string
Definition: forward.h:123
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
void closeForReading(const sfileno fileno)
close slot after reading, decrements read level
Definition: MemMap.cc:207
class SquidConfig Config
Definition: SquidConfig.cc:12
unsigned long LibErrorCode
TLS library-reported non-validation error.
Definition: forward.h:112
void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &)
Definition: Session.cc:246

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors