]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/Session.cc
Update to options= after audit
[thirdparty/squid.git] / src / security / Session.cc
CommitLineData
824d4656 1/*
4ac4a490 2 * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
824d4656
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
5d9a65df
AJ
9/* DEBUG: section 83 TLS session management */
10
824d4656
AJ
11#include "squid.h"
12#include "anyp/PortCfg.h"
13#include "base/RunnersRegistry.h"
fd25ebc0 14#include "CachePeer.h"
5d9a65df 15#include "Debug.h"
087b94cb 16#include "fd.h"
86f77270 17#include "fde.h"
824d4656
AJ
18#include "ipc/MemMap.h"
19#include "security/Session.h"
20#include "SquidConfig.h"
86f77270 21#include "ssl/bio.h"
824d4656
AJ
22
23#define SSL_SESSION_ID_SIZE 32
24#define SSL_SESSION_MAX_SIZE 10*1024
25
087b94cb
AJ
26#if USE_OPENSSL || USE_GNUTLS
27static int
28tls_read_method(int fd, char *buf, int len)
29{
30 auto session = fd_table[fd].ssl.get();
c824e69a 31 debugs(83, 3, "started for session=" << (void*)session);
087b94cb
AJ
32
33#if DONT_DO_THIS && USE_OPENSSL
34 if (!SSL_is_init_finished(session)) {
35 errno = ENOTCONN;
36 return -1;
37 }
38#endif
39
40#if USE_OPENSSL
41 int i = SSL_read(session, buf, len);
42#elif USE_GNUTLS
43 int i = gnutls_record_recv(session, buf, len);
44#endif
fd25ebc0 45
087b94cb 46 if (i > 0) {
9c8549cf 47 debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
087b94cb
AJ
48 (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
49 }
50
51#if USE_OPENSSL
52 if (i > 0 && SSL_pending(session) > 0) {
53#elif USE_GNUTLS
54 if (i > 0 && gnutls_record_check_pending(session) > 0) {
55#endif
56 debugs(83, 2, "TLS FD " << fd << " is pending");
57 fd_table[fd].flags.read_pending = true;
58 } else
59 fd_table[fd].flags.read_pending = false;
60
61 return i;
62}
63
64static int
65tls_write_method(int fd, const char *buf, int len)
66{
67 auto session = fd_table[fd].ssl.get();
f5ee00c0 68 debugs(83, 3, "started for session=" << (void*)session);
087b94cb
AJ
69
70#if USE_OPENSSL
71 if (!SSL_is_init_finished(session)) {
72 errno = ENOTCONN;
73 return -1;
74 }
75#endif
76
77#if USE_OPENSSL
78 int i = SSL_write(session, buf, len);
79#elif USE_GNUTLS
80 int i = gnutls_record_send(session, buf, len);
81#endif
82
9c8549cf
AJ
83 if (i > 0) {
84 debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
85 }
087b94cb
AJ
86 return i;
87}
88#endif
89
86f77270
AJ
90static bool
91CreateSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &conn, Security::Io::Type type, const char *squidCtx)
92{
93 if (!Comm::IsConnOpen(conn)) {
94 debugs(83, DBG_IMPORTANT, "Gone connection");
95 return false;
96 }
97
087b94cb
AJ
98#if USE_OPENSSL || USE_GNUTLS
99
86f77270 100 const char *errAction = "with no TLS/SSL library";
86f77270 101 int errCode = 0;
e0cf4700 102#if USE_OPENSSL
9c8549cf
AJ
103 Security::SessionPointer session(SSL_new(ctx.get()), [](SSL *p) {
104 debugs(83, 5, "SSL_free session=" << (void*)p);
105 SSL_free(p);
106 });
107 debugs(83, 5, "SSL_new session=" << (void*)session.get());
087b94cb
AJ
108 if (!session) {
109 errCode = ERR_get_error();
110 errAction = "failed to allocate handle";
111 }
112#elif USE_GNUTLS
113 gnutls_session_t tmp;
bd649d05 114 errCode = gnutls_init(&tmp, static_cast<unsigned int>(type) | GNUTLS_NONBLOCK);
9c8549cf
AJ
115 Security::SessionPointer session(tmp, [](gnutls_session_t p) {
116 debugs(83, 5, "gnutls_deinit session=" << (void*)p);
89412a69 117 gnutls_deinit(p);
9c8549cf 118 });
bd649d05 119 debugs(83, 5, "gnutls_init " << (type == Security::Io::BIO_TO_SERVER ? "client" : "server" )<< " session=" << (void*)session.get());
087b94cb
AJ
120 if (errCode != GNUTLS_E_SUCCESS) {
121 session.reset();
122 errAction = "failed to initialize session";
123 }
124#endif
125
126 if (session) {
86f77270 127 const int fd = conn->fd;
087b94cb
AJ
128
129#if USE_OPENSSL
d0aafe66 130 // without BIO, we would call SSL_set_fd(ssl.get(), fd) instead
86f77270 131 if (BIO *bio = Ssl::Bio::Create(fd, type)) {
087b94cb
AJ
132 Ssl::Bio::Link(session.get(), bio); // cannot fail
133#elif USE_GNUTLS
134 errCode = gnutls_credentials_set(session.get(), GNUTLS_CRD_CERTIFICATE, ctx.get());
135 if (errCode == GNUTLS_E_SUCCESS) {
fd25ebc0
AJ
136
137 if (auto *peer = conn->getPeer())
138 peer->secure.updateSessionOptions(session);
139 else
140 Security::ProxyOutgoingConfig.updateSessionOptions(session);
141
142 // NP: GnuTLS does not yet support the BIO operations
143 // this does the equivalent of SSL_set_fd() for now.
144 gnutls_transport_set_int(session.get(), fd);
d48f33e3 145 gnutls_handshake_set_timeout(session.get(), GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
087b94cb 146#endif
86f77270 147
9c8549cf 148 debugs(83, 5, "link FD " << fd << " to TLS session=" << (void*)session.get());
087b94cb
AJ
149 fd_table[fd].ssl = session;
150 fd_table[fd].read_method = &tls_read_method;
151 fd_table[fd].write_method = &tls_write_method;
86f77270
AJ
152 fd_note(fd, squidCtx);
153 return true;
154 }
087b94cb
AJ
155
156#if USE_OPENSSL
86f77270
AJ
157 errCode = ERR_get_error();
158 errAction = "failed to initialize I/O";
087b94cb
AJ
159#elif USE_GNUTLS
160 errAction = "failed to assign credentials";
86f77270 161#endif
087b94cb
AJ
162 }
163
e0cf4700
AJ
164 debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
165 ": " << (errCode != 0 ? Security::ErrorString(errCode) : ""));
087b94cb 166#endif
86f77270
AJ
167 return false;
168}
169
170bool
171Security::CreateClientSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
172{
173 return CreateSession(ctx, c, Security::Io::BIO_TO_SERVER, squidCtx);
174}
175
176bool
177Security::CreateServerSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
178{
179 return CreateSession(ctx, c, Security::Io::BIO_TO_CLIENT, squidCtx);
180}
181
087b94cb 182void
9c8549cf 183Security::SessionClose(const Security::SessionPointer &s, const int fdOnError)
087b94cb
AJ
184{
185 debugs(83, 5, "session=" << (void*)s.get());
9c8549cf 186 if (s && fdOnError == -1) {
087b94cb
AJ
187#if USE_OPENSSL
188 SSL_shutdown(s.get());
189#elif USE_GNUTLS
190 gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
087b94cb 191 }
9c8549cf 192
9c8549cf
AJ
193 // XXX: should probably be done for OpenSSL too, but that needs testing.
194 if (fdOnError != -1) {
195 debugs(83, 5, "unlink FD " << fdOnError << " from TLS session=" << (void*)fd_table[fdOnError].ssl.get());
196 fd_table[fdOnError].ssl.reset();
197 fd_table[fdOnError].read_method = &default_read_method;
198 fd_table[fdOnError].write_method = &default_write_method;
199 fd_note(fdOnError, "TLS error");
9c8549cf 200#endif
6a0f6036 201 }
087b94cb
AJ
202}
203
5d9a65df
AJ
204bool
205Security::SessionIsResumed(const Security::SessionPointer &s)
206{
207 bool result = false;
208#if USE_OPENSSL
209 result = SSL_session_reused(s.get()) == 1;
210#elif USE_GNUTLS
211 result = gnutls_session_is_resumed(s.get()) != 0;
212#endif
213 debugs(83, 7, "session=" << (void*)s.get() << ", query? answer: " << (result ? 'T' : 'F') );
214 return result;
215}
216
217void
218Security::MaybeGetSessionResumeData(const Security::SessionPointer &s, Security::SessionStatePointer &data)
219{
220 if (!SessionIsResumed(s)) {
221#if USE_OPENSSL
222 // nil is valid for SSL_get1_session(), it cannot fail.
223 data.reset(SSL_get1_session(s.get()));
224#elif USE_GNUTLS
225 gnutls_datum_t *tmp = nullptr;
226 const auto x = gnutls_session_get_data2(s.get(), tmp);
227 if (x != GNUTLS_E_SUCCESS) {
ea574635 228 debugs(83, 3, "session=" << (void*)s.get() << " error: " << Security::ErrorString(x));
5d9a65df
AJ
229 }
230 data.reset(tmp);
231#endif
232 debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get());
233 } else {
234 debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get() << ", do nothing.");
235 }
236}
237
238void
239Security::SetSessionResumeData(const Security::SessionPointer &s, const Security::SessionStatePointer &data)
240{
241 if (data) {
242#if USE_OPENSSL
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() <<
ea574635 246 " resume error: " << Security::ErrorString(ssl_error));
5d9a65df
AJ
247 }
248#elif USE_GNUTLS
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() <<
ea574635 252 " resume error: " << Security::ErrorString(x));
5d9a65df
AJ
253 }
254#else
255 // critical because, how did it get here?
256 debugs(83, DBG_CRITICAL, "no TLS library. session=" << (void*)s.get() << " data=" << (void*)data.get());
257#endif
258 debugs(83, 5, "session=" << (void*)s.get() << " data=" << (void*)data.get());
259 } else {
260 debugs(83, 5, "session=" << (void*)s.get() << " no resume data");
261 }
262}
263
824d4656
AJ
264static bool
265isTlsServer()
266{
267 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
268 if (s->secure.encryptTransport)
269 return true;
270 if (s->flags.tunnelSslBumping)
271 return true;
272 }
273
274 return false;
275}
276
277void
278initializeSessionCache()
279{
280#if USE_OPENSSL
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);
285
286 int configuredItems = ::Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot);
287 if (IamWorkerProcess() && configuredItems)
288 Ssl::SessionCache = new Ipc::MemMap(Ssl::SessionCacheName);
289 else {
290 Ssl::SessionCache = nullptr;
291 return;
292 }
293
294 for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
b23f5f9c
AJ
295 if (s->secure.staticContext)
296 Ssl::SetSessionCallbacks(s->secure.staticContext);
824d4656
AJ
297 }
298#endif
299}
300
301/// initializes shared memory segments used by MemStore
302class SharedSessionCacheRr: public Ipc::Mem::RegisteredRunner
303{
304public:
305 /* RegisteredRunner API */
306 SharedSessionCacheRr(): owner(nullptr) {}
307 virtual void useConfig();
308 virtual ~SharedSessionCacheRr();
309
310protected:
311 virtual void create();
312
313private:
314 Ipc::MemMap::Owner *owner;
315};
316
317RunnerRegistrationEntry(SharedSessionCacheRr);
318
319void
320SharedSessionCacheRr::useConfig()
321{
322#if USE_OPENSSL // while Ssl:: bits in use
323 if (Ssl::SessionCache || !isTlsServer()) //no need to configure ssl session cache.
324 return;
325
326 Ipc::Mem::RegisteredRunner::useConfig();
327 initializeSessionCache();
328#endif
329}
330
331void
332SharedSessionCacheRr::create()
333{
334 if (!isTlsServer()) //no need to configure ssl session cache.
335 return;
336
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);
340#endif
341}
342
343SharedSessionCacheRr::~SharedSessionCacheRr()
344{
345 // XXX: Enable after testing to reduce at-exit memory "leaks".
346 // delete Ssl::SessionCache;
347
348 delete owner;
349}
350