]> git.ipfire.org Git - thirdparty/squid.git/blob - src/security/Session.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / security / Session.cc
1 /*
2 * Copyright (C) 1996-2017 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 || USE_GNUTLS
27 static int
28 tls_read_method(int fd, char *buf, int len)
29 {
30 auto session = fd_table[fd].ssl.get();
31 debugs(83, 3, "started for session=" << (void*)session);
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
45
46 if (i > 0) {
47 debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
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
64 static int
65 tls_write_method(int fd, const char *buf, int len)
66 {
67 auto session = fd_table[fd].ssl.get();
68 debugs(83, 3, "started for session=" << (void*)session);
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
83 if (i > 0) {
84 debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
85 }
86 return i;
87 }
88 #endif
89
90 #if USE_OPENSSL
91 Security::SessionPointer
92 Security::NewSessionObject(const Security::ContextPointer &ctx)
93 {
94 Security::SessionPointer session(SSL_new(ctx.get()), [](SSL *p) {
95 debugs(83, 5, "SSL_free session=" << (void*)p);
96 SSL_free(p);
97 });
98 debugs(83, 5, "SSL_new session=" << (void*)session.get());
99 return session;
100 }
101 #endif
102
103 static bool
104 CreateSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &conn, Security::Io::Type type, const char *squidCtx)
105 {
106 if (!Comm::IsConnOpen(conn)) {
107 debugs(83, DBG_IMPORTANT, "Gone connection");
108 return false;
109 }
110
111 #if USE_OPENSSL || USE_GNUTLS
112
113 const char *errAction = "with no TLS/SSL library";
114 int errCode = 0;
115 #if USE_OPENSSL
116 Security::SessionPointer session(Security::NewSessionObject(ctx));
117 if (!session) {
118 errCode = ERR_get_error();
119 errAction = "failed to allocate handle";
120 }
121 #elif USE_GNUTLS
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);
126 gnutls_deinit(p);
127 });
128 debugs(83, 5, "gnutls_init " << (type == Security::Io::BIO_TO_SERVER ? "client" : "server" )<< " session=" << (void*)session.get());
129 if (errCode != GNUTLS_E_SUCCESS) {
130 session.reset();
131 errAction = "failed to initialize session";
132 }
133 #endif
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 if (auto *peer = conn->getPeer())
147 peer->secure.updateSessionOptions(session);
148 else
149 Security::ProxyOutgoingConfig.updateSessionOptions(session);
150
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);
155 #endif
156
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);
162 return true;
163 }
164
165 #if USE_OPENSSL
166 errCode = ERR_get_error();
167 errAction = "failed to initialize I/O";
168 #elif USE_GNUTLS
169 errAction = "failed to assign credentials";
170 #endif
171 }
172
173 debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
174 ": " << (errCode != 0 ? Security::ErrorString(errCode) : ""));
175 #endif
176 return false;
177 }
178
179 bool
180 Security::CreateClientSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
181 {
182 return CreateSession(ctx, c, Security::Io::BIO_TO_SERVER, squidCtx);
183 }
184
185 bool
186 Security::CreateServerSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
187 {
188 return CreateSession(ctx, c, Security::Io::BIO_TO_CLIENT, squidCtx);
189 }
190
191 void
192 Security::SessionSendGoodbye(const Security::SessionPointer &s)
193 {
194 debugs(83, 5, "session=" << (void*)s.get());
195 if (s) {
196 #if USE_OPENSSL
197 SSL_shutdown(s.get());
198 #elif USE_GNUTLS
199 gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
200 #endif
201 }
202 }
203
204 bool
205 Security::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
217 void
218 Security::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) {
228 debugs(83, 3, "session=" << (void*)s.get() << " error: " << Security::ErrorString(x));
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
238 void
239 Security::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() <<
246 " resume error: " << Security::ErrorString(ssl_error));
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() <<
252 " resume error: " << Security::ErrorString(x));
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
264 static bool
265 isTlsServer()
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
277 void
278 initializeSessionCache()
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) {
295 if (s->secure.staticContext)
296 Ssl::SetSessionCallbacks(s->secure.staticContext);
297 }
298 #endif
299 }
300
301 /// initializes shared memory segments used by MemStore
302 class SharedSessionCacheRr: public Ipc::Mem::RegisteredRunner
303 {
304 public:
305 /* RegisteredRunner API */
306 SharedSessionCacheRr(): owner(nullptr) {}
307 virtual void useConfig();
308 virtual ~SharedSessionCacheRr();
309
310 protected:
311 virtual void create();
312
313 private:
314 Ipc::MemMap::Owner *owner;
315 };
316
317 RunnerRegistrationEntry(SharedSessionCacheRr);
318
319 void
320 SharedSessionCacheRr::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
331 void
332 SharedSessionCacheRr::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
343 SharedSessionCacheRr::~SharedSessionCacheRr()
344 {
345 // XXX: Enable after testing to reduce at-exit memory "leaks".
346 // delete Ssl::SessionCache;
347
348 delete owner;
349 }
350