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

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors