2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 83 TLS session management */
12 #include "anyp/PortCfg.h"
13 #include "base/RunnersRegistry.h"
14 #include "CachePeer.h"
18 #include "ipc/MemMap.h"
19 #include "security/Session.h"
20 #include "SquidConfig.h"
23 #define SSL_SESSION_ID_SIZE 32
24 #define SSL_SESSION_MAX_SIZE 10*1024
26 #if USE_OPENSSL || USE_GNUTLS
28 tls_read_method(int fd
, char *buf
, int len
)
30 auto session
= fd_table
[fd
].ssl
.get();
31 debugs(83, 3, "started for session=" << (void*)session
);
33 #if DONT_DO_THIS && USE_OPENSSL
34 if (!SSL_is_init_finished(session
)) {
41 int i
= SSL_read(session
, buf
, len
);
43 int i
= gnutls_record_recv(session
, buf
, len
);
47 debugs(83, 8, "TLS FD " << fd
<< " session=" << (void*)session
<< " " << i
<< " bytes");
48 (void)VALGRIND_MAKE_MEM_DEFINED(buf
, i
);
52 if (i
> 0 && SSL_pending(session
) > 0) {
54 if (i
> 0 && gnutls_record_check_pending(session
) > 0) {
56 debugs(83, 2, "TLS FD " << fd
<< " is pending");
57 fd_table
[fd
].flags
.read_pending
= true;
59 fd_table
[fd
].flags
.read_pending
= false;
65 tls_write_method(int fd
, const char *buf
, int len
)
67 auto session
= fd_table
[fd
].ssl
.get();
68 debugs(83, 3, "started for session=" << (void*)session
);
71 if (!SSL_is_init_finished(session
)) {
78 int i
= SSL_write(session
, buf
, len
);
80 int i
= gnutls_record_send(session
, buf
, len
);
84 debugs(83, 8, "TLS FD " << fd
<< " session=" << (void*)session
<< " " << i
<< " bytes");
91 Security::SessionPointer
92 Security::NewSessionObject(const Security::ContextPointer
&ctx
)
94 Security::SessionPointer
session(SSL_new(ctx
.get()), [](SSL
*p
) {
95 debugs(83, 5, "SSL_free session=" << (void*)p
);
98 debugs(83, 5, "SSL_new session=" << (void*)session
.get());
104 CreateSession(const Security::ContextPointer
&ctx
, const Comm::ConnectionPointer
&conn
, Security::Io::Type type
, const char *squidCtx
)
106 if (!Comm::IsConnOpen(conn
)) {
107 debugs(83, DBG_IMPORTANT
, "Gone connection");
111 #if USE_OPENSSL || USE_GNUTLS
113 const char *errAction
= "with no TLS/SSL library";
116 Security::SessionPointer
session(Security::NewSessionObject(ctx
));
118 errCode
= ERR_get_error();
119 errAction
= "failed to allocate handle";
122 gnutls_session_t tmp
;
123 errCode
= gnutls_init(&tmp
, static_cast<unsigned int>(type
) | GNUTLS_NONBLOCK
);
124 Security::SessionPointer
session(tmp
, [](gnutls_session_t p
) {
125 debugs(83, 5, "gnutls_deinit session=" << (void*)p
);
128 debugs(83, 5, "gnutls_init " << (type
== Security::Io::BIO_TO_SERVER
? "client" : "server" )<< " session=" << (void*)session
.get());
129 if (errCode
!= GNUTLS_E_SUCCESS
) {
131 errAction
= "failed to initialize session";
136 const int fd
= conn
->fd
;
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
143 errCode
= gnutls_credentials_set(session
.get(), GNUTLS_CRD_CERTIFICATE
, ctx
.get());
144 if (errCode
== GNUTLS_E_SUCCESS
) {
146 if (auto *peer
= conn
->getPeer())
147 peer
->secure
.updateSessionOptions(session
);
149 Security::ProxyOutgoingConfig
.updateSessionOptions(session
);
151 // NP: GnuTLS does not yet support the BIO operations
152 // this does the equivalent of SSL_set_fd() for now.
153 gnutls_transport_set_int(session
.get(), fd
);
154 gnutls_handshake_set_timeout(session
.get(), GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT
);
157 debugs(83, 5, "link FD " << fd
<< " to TLS session=" << (void*)session
.get());
158 fd_table
[fd
].ssl
= session
;
159 fd_table
[fd
].read_method
= &tls_read_method
;
160 fd_table
[fd
].write_method
= &tls_write_method
;
161 fd_note(fd
, squidCtx
);
166 errCode
= ERR_get_error();
167 errAction
= "failed to initialize I/O";
169 errAction
= "failed to assign credentials";
173 debugs(83, DBG_IMPORTANT
, "ERROR: " << squidCtx
<< ' ' << errAction
<<
174 ": " << (errCode
!= 0 ? Security::ErrorString(errCode
) : ""));
180 Security::CreateClientSession(const Security::ContextPointer
&ctx
, const Comm::ConnectionPointer
&c
, const char *squidCtx
)
182 return CreateSession(ctx
, c
, Security::Io::BIO_TO_SERVER
, squidCtx
);
186 Security::CreateServerSession(const Security::ContextPointer
&ctx
, const Comm::ConnectionPointer
&c
, const char *squidCtx
)
188 return CreateSession(ctx
, c
, Security::Io::BIO_TO_CLIENT
, squidCtx
);
192 Security::SessionSendGoodbye(const Security::SessionPointer
&s
)
194 debugs(83, 5, "session=" << (void*)s
.get());
197 SSL_shutdown(s
.get());
199 gnutls_bye(s
.get(), GNUTLS_SHUT_RDWR
);
205 Security::SessionIsResumed(const Security::SessionPointer
&s
)
209 result
= SSL_session_reused(s
.get()) == 1;
211 result
= gnutls_session_is_resumed(s
.get()) != 0;
213 debugs(83, 7, "session=" << (void*)s
.get() << ", query? answer: " << (result
? 'T' : 'F') );
218 Security::MaybeGetSessionResumeData(const Security::SessionPointer
&s
, Security::SessionStatePointer
&data
)
220 if (!SessionIsResumed(s
)) {
222 // nil is valid for SSL_get1_session(), it cannot fail.
223 data
.reset(SSL_get1_session(s
.get()));
225 gnutls_datum_t
*tmp
= nullptr;
226 const auto x
= gnutls_session_get_data2(s
.get(), tmp
);
227 if (x
!= GNUTLS_E_SUCCESS
) {
228 debugs(83, 3, "session=" << (void*)s
.get() << " error: " << Security::ErrorString(x
));
232 debugs(83, 5, "session=" << (void*)s
.get() << " data=" << (void*)data
.get());
234 debugs(83, 5, "session=" << (void*)s
.get() << " data=" << (void*)data
.get() << ", do nothing.");
239 Security::SetSessionResumeData(const Security::SessionPointer
&s
, const Security::SessionStatePointer
&data
)
243 if (!SSL_set_session(s
.get(), data
.get())) {
244 const auto ssl_error
= ERR_get_error();
245 debugs(83, 3, "session=" << (void*)s
.get() << " data=" << (void*)data
.get() <<
246 " resume error: " << Security::ErrorString(ssl_error
));
249 const auto x
= gnutls_session_set_data(s
.get(), data
->data
, data
->size
);
250 if (x
!= GNUTLS_E_SUCCESS
) {
251 debugs(83, 3, "session=" << (void*)s
.get() << " data=" << (void*)data
.get() <<
252 " resume error: " << Security::ErrorString(x
));
255 // critical because, how did it get here?
256 debugs(83, DBG_CRITICAL
, "no TLS library. session=" << (void*)s
.get() << " data=" << (void*)data
.get());
258 debugs(83, 5, "session=" << (void*)s
.get() << " data=" << (void*)data
.get());
260 debugs(83, 5, "session=" << (void*)s
.get() << " no resume data");
267 for (AnyP::PortCfgPointer s
= HttpPortList
; s
!= nullptr; s
= s
->next
) {
268 if (s
->secure
.encryptTransport
)
270 if (s
->flags
.tunnelSslBumping
)
278 initializeSessionCache()
281 // Check if the MemMap keys and data are enough big to hold
282 // session ids and session data
283 assert(SSL_SESSION_ID_SIZE
>= MEMMAP_SLOT_KEY_SIZE
);
284 assert(SSL_SESSION_MAX_SIZE
>= MEMMAP_SLOT_DATA_SIZE
);
286 int configuredItems
= ::Config
.SSL
.sessionCacheSize
/ sizeof(Ipc::MemMap::Slot
);
287 if (IamWorkerProcess() && configuredItems
)
288 Ssl::SessionCache
= new Ipc::MemMap(Ssl::SessionCacheName
);
290 Ssl::SessionCache
= nullptr;
294 for (AnyP::PortCfgPointer s
= HttpPortList
; s
!= nullptr; s
= s
->next
) {
295 if (s
->secure
.staticContext
)
296 Ssl::SetSessionCallbacks(s
->secure
.staticContext
);
301 /// initializes shared memory segments used by MemStore
302 class SharedSessionCacheRr
: public Ipc::Mem::RegisteredRunner
305 /* RegisteredRunner API */
306 SharedSessionCacheRr(): owner(nullptr) {}
307 virtual void useConfig();
308 virtual ~SharedSessionCacheRr();
311 virtual void create();
314 Ipc::MemMap::Owner
*owner
;
317 RunnerRegistrationEntry(SharedSessionCacheRr
);
320 SharedSessionCacheRr::useConfig()
322 #if USE_OPENSSL // while Ssl:: bits in use
323 if (Ssl::SessionCache
|| !isTlsServer()) //no need to configure ssl session cache.
326 Ipc::Mem::RegisteredRunner::useConfig();
327 initializeSessionCache();
332 SharedSessionCacheRr::create()
334 if (!isTlsServer()) //no need to configure ssl session cache.
337 #if USE_OPENSSL // while Ssl:: bits in use
338 if (int items
= Config
.SSL
.sessionCacheSize
/ sizeof(Ipc::MemMap::Slot
))
339 owner
= Ipc::MemMap::Init(Ssl::SessionCacheName
, items
);
343 SharedSessionCacheRr::~SharedSessionCacheRr()
345 // XXX: Enable after testing to reduce at-exit memory "leaks".
346 // delete Ssl::SessionCache;