Line data Source code
1 : // Copyright (c) 2012-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/eventdispatcher
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief Event dispatch class.
22 : *
23 : * Class used to handle events.
24 : */
25 :
26 : // make sure we use OpenSSL with multi-thread support
27 : // (TODO: move to .cpp once we have the impl!)
28 : #define OPENSSL_THREAD_DEFINES
29 :
30 : // self
31 : //
32 : #include "eventdispatcher/tcp_bio_server.h"
33 :
34 : #include "eventdispatcher/exception.h"
35 : #include "eventdispatcher/tcp_private.h"
36 :
37 :
38 : // snaplogger lib
39 : //
40 : #include <snaplogger/message.h>
41 :
42 :
43 : // snapdev lib
44 : //
45 : #include <snapdev/not_used.h>
46 :
47 :
48 : // C++ lib
49 : //
50 : #include <algorithm>
51 :
52 :
53 : // OpenSSL lib
54 : //
55 : #include <openssl/ssl.h>
56 :
57 :
58 : // C lib
59 : //
60 : #include <fcntl.h>
61 :
62 :
63 : // last include
64 : //
65 : #include <snapdev/poison.h>
66 :
67 :
68 :
69 :
70 : #ifndef OPENSSL_THREADS
71 : #error "OPENSSL_THREADS is not defined. Snap! requires support for multiple threads in OpenSSL."
72 : #endif
73 :
74 : namespace ed
75 : {
76 :
77 :
78 : namespace detail
79 : {
80 :
81 :
82 0 : class tcp_bio_server_impl
83 : {
84 : public:
85 : int f_max_connections = MAX_CONNECTIONS;
86 : std::shared_ptr<SSL_CTX> f_ssl_ctx = std::shared_ptr<SSL_CTX>();
87 : std::shared_ptr<BIO> f_listen = std::shared_ptr<BIO>();
88 : bool f_keepalive = true;
89 : bool f_close_on_exec = false;
90 : };
91 :
92 :
93 : }
94 :
95 :
96 : /** \class tcp_bio_server
97 : * \brief Create a BIO server, bind it, and listen for connections.
98 : *
99 : * This class is a server socket implementation used to listen for
100 : * connections that are to use TLS encryptions.
101 : *
102 : * The bind address must be available for the server initialization
103 : * to succeed.
104 : *
105 : * The BIO extension is from the OpenSSL library and it allows the server
106 : * to allow connections using SSL (TLS really now a day). The server
107 : * expects to be given information about a certificate and a private
108 : * key to function. You may also use the server in a non-secure manner
109 : * (without the TLS layer) so you do not need to implement two instances
110 : * of your server, one with tcp_bio_server and one with tcp_server.
111 : */
112 :
113 :
114 :
115 :
116 : /** \brief Construct a tcp_bio_server object.
117 : *
118 : * The tcp_bio_server constructor initializes a BIO server and listens
119 : * for connections from the specified address and port.
120 : *
121 : * The \p certificate and \p private_key filenames are expected to point
122 : * to a PEM file (.pem extension) that include the encryption information.
123 : *
124 : * The certificate file may include a chain in which case the whole chain
125 : * will be taken in account.
126 : *
127 : * \warning
128 : * Currently the max_connections parameter is pretty much ignored since
129 : * there is no way to pass that parameter down to the BIO interface. In
130 : * that code they use the SOMAXCONN definition which under Linux is
131 : * defined at 128 (Ubuntu 16.04.1). See:
132 : * /usr/include/x86_64-linux-gnu/bits/socket.h
133 : *
134 : * \param[in] addr_port The address and port defined in an addr object.
135 : * \param[in] max_connections The number of connections to keep in the listen queue.
136 : * \param[in] reuse_addr Whether to mark the socket with the SO_REUSEADDR flag.
137 : * \param[in] certificate The server certificate filename (PEM).
138 : * \param[in] private_key The server private key filename (PEM).
139 : * \param[in] mode The mode used to create the listening socket.
140 : */
141 0 : tcp_bio_server::tcp_bio_server(
142 : addr::addr const & addr_port
143 : , int max_connections
144 : , bool reuse_addr
145 : , std::string const & certificate
146 : , std::string const & private_key
147 0 : , mode_t mode)
148 0 : : f_impl(std::make_shared<detail::tcp_bio_server_impl>())
149 : {
150 : #if __GNUC__ > 5
151 0 : f_impl->f_max_connections = std::clamp(max_connections <= 0 ? MAX_CONNECTIONS : max_connections, 5, 1000);
152 : #else
153 : f_impl->f_max_connections = max_connections <= 0 ? MAX_CONNECTIONS : max_connections;
154 : if(f_impl->f_max_connections < 5)
155 : {
156 : f_impl->f_max_connections = 5;
157 : }
158 : else if(f_impl->f_max_connections > 1000)
159 : {
160 : f_impl->f_max_connections = 1000;
161 : }
162 : #endif
163 :
164 0 : detail::bio_initialize();
165 :
166 0 : switch(mode)
167 : {
168 0 : case mode_t::MODE_SECURE:
169 : {
170 : // the following code is based on the example shown in the man page
171 : //
172 : // man BIO_f_ssl
173 : //
174 0 : if(certificate.empty()
175 0 : || private_key.empty())
176 : {
177 0 : throw event_dispatcher_initialization_error("with MODE_SECURE you must specify a certificate and a private_key filename");
178 : }
179 :
180 0 : std::shared_ptr<SSL_CTX> ssl_ctx; // use reset(), see SNAP-507
181 0 : ssl_ctx.reset(SSL_CTX_new(SSLv23_server_method()), detail::ssl_ctx_deleter);
182 0 : if(!ssl_ctx)
183 : {
184 0 : detail::bio_log_errors();
185 0 : throw event_dispatcher_initialization_error("failed creating an SSL_CTX server object");
186 : }
187 :
188 0 : SSL_CTX_set_cipher_list(ssl_ctx.get(), "ALL");//"HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
189 :
190 : // Assign the certificate to the SSL context
191 : //
192 : // TBD: we may want to use SSL_CTX_use_certificate_file() instead
193 : // (i.e. not the "chained" version)
194 : //
195 0 : if(!SSL_CTX_use_certificate_chain_file(ssl_ctx.get(), certificate.c_str()))
196 : {
197 0 : detail::bio_log_errors();
198 0 : throw event_dispatcher_initialization_error("failed initializing an SSL_CTX server object certificate");
199 : }
200 :
201 : // Assign the private key to the SSL context
202 : //
203 0 : if(!SSL_CTX_use_PrivateKey_file(ssl_ctx.get(), private_key.c_str(), SSL_FILETYPE_PEM))
204 : {
205 : // on failure, try again again with the RSA version, just in case
206 : // (probably useless?)
207 : //
208 0 : if(!SSL_CTX_use_RSAPrivateKey_file(ssl_ctx.get(), private_key.c_str(), SSL_FILETYPE_PEM))
209 : {
210 0 : detail::bio_log_errors();
211 0 : throw event_dispatcher_initialization_error("failed initializing an SSL_CTX server object private key");
212 : }
213 : }
214 :
215 : // Verify that the private key and certificate are a match
216 : //
217 0 : if(!SSL_CTX_check_private_key(ssl_ctx.get()))
218 : {
219 0 : detail::bio_log_errors();
220 0 : throw event_dispatcher_initialization_error("failed initializing an SSL_CTX server object private key");
221 : }
222 :
223 : // create a BIO connection with SSL
224 : //
225 0 : std::unique_ptr<BIO, void (*)(BIO *)> bio(BIO_new_ssl(ssl_ctx.get(), 0), detail::bio_deleter);
226 0 : if(!bio)
227 : {
228 0 : detail::bio_log_errors();
229 0 : throw event_dispatcher_initialization_error("failed initializing a BIO server object");
230 : }
231 :
232 : // get the SSL pointer, which generally means that the BIO
233 : // allocate succeeded fully, so we can set auto-retry
234 : //
235 0 : SSL * ssl(nullptr);
236 : #pragma GCC diagnostic push
237 : #pragma GCC diagnostic ignored "-Wold-style-cast"
238 0 : BIO_get_ssl(bio.get(), &ssl);
239 : #pragma GCC diagnostic pop
240 0 : if(ssl == nullptr)
241 : {
242 : // TBD: does this mean we would have a plain connection?
243 0 : detail::bio_log_errors();
244 0 : throw event_dispatcher_initialization_error("failed connecting BIO object with SSL_CTX object");
245 : }
246 :
247 : // allow automatic retries in case the connection somehow needs
248 : // an SSL renegotiation (maybe we should turn that off for cases
249 : // where we connect to a secure payment gateway?)
250 : //
251 0 : SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
252 :
253 : // create a listening connection
254 : //
255 0 : std::shared_ptr<BIO> listen; // use reset(), see SNAP-507
256 0 : listen.reset(BIO_new_accept(addr_port.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_PORT).c_str()), detail::bio_deleter);
257 0 : if(!listen)
258 : {
259 0 : detail::bio_log_errors();
260 0 : throw event_dispatcher_initialization_error("failed initializing a BIO server object");
261 : }
262 :
263 0 : BIO_set_bind_mode(listen.get(), reuse_addr ? BIO_BIND_REUSEADDR : BIO_BIND_NORMAL);
264 :
265 : // Attach the SSL bio to the listening BIO, this means whenever
266 : // a new connection is accepted, it automatically attaches it to
267 : // an SSL connection
268 : //
269 : #pragma GCC diagnostic push
270 : #pragma GCC diagnostic ignored "-Wold-style-cast"
271 0 : BIO_set_accept_bios(listen.get(), bio.get());
272 : #pragma GCC diagnostic pop
273 :
274 : // WARNING: the listen object takes ownership of the `bio`
275 : // pointer and thus we have to make sure that we
276 : // do not keep it in our unique_ptr<>().
277 : //
278 0 : snap::NOT_USED(bio.release());
279 :
280 : // Actually call bind() and listen() on the socket
281 : //
282 : // IMPORTANT NOTE: The BIO_do_accept() is overloaded, it does
283 : // two things: (a) it bind() + listen() when called the very
284 : // first time (i.e. the call right here); (b) it actually
285 : // accepts a client connection
286 : //
287 0 : int const r(BIO_do_accept(listen.get()));
288 0 : if(r <= 0)
289 : {
290 0 : detail::bio_log_errors();
291 0 : throw event_dispatcher_initialization_error("failed initializing the BIO server socket to listen for client connections");
292 : }
293 :
294 : // it worked, save the results
295 0 : f_impl->f_ssl_ctx.swap(ssl_ctx);
296 0 : f_impl->f_listen.swap(listen);
297 :
298 : // secure connection ready
299 : }
300 0 : break;
301 :
302 0 : case mode_t::MODE_PLAIN:
303 : {
304 0 : std::shared_ptr<BIO> listen; // use reset(), see SNAP-507
305 0 : listen.reset(BIO_new_accept(addr_port.to_ipv4or6_string(addr::addr::string_ip_t::STRING_IP_PORT).c_str()), detail::bio_deleter);
306 0 : if(!listen)
307 : {
308 0 : detail::bio_log_errors();
309 0 : throw event_dispatcher_initialization_error("failed initializing a BIO server object");
310 : }
311 :
312 0 : BIO_set_bind_mode(listen.get(), BIO_BIND_REUSEADDR);
313 :
314 : // Actually call bind() and listen() on the socket
315 : //
316 : // IMPORTANT NOTE: The BIO_do_accept() is overloaded, it does
317 : // two things: (a) it bind() + listen() when called the very
318 : // first time (i.e. the call right here); (b) it actually
319 : // accepts a client connection
320 : //
321 0 : int const r(BIO_do_accept(listen.get()));
322 0 : if(r <= 0)
323 : {
324 0 : detail::bio_log_errors();
325 0 : throw event_dispatcher_initialization_error("failed initializing the BIO server socket to listen for client connections");
326 : }
327 :
328 : // it worked, save the results
329 : //
330 0 : f_impl->f_listen.swap(listen);
331 : }
332 0 : break;
333 :
334 : }
335 0 : }
336 :
337 :
338 : /** \brief Clean up the TCP Bio Server object.
339 : *
340 : * This function cleans up the object on destruction.
341 : */
342 0 : tcp_bio_server::~tcp_bio_server()
343 : {
344 0 : }
345 :
346 :
347 : /** \brief Return the current status of the keepalive flag.
348 : *
349 : * This function returns the current status of the keepalive flag. This
350 : * flag is set to true by default (in the constructor.) It can be
351 : * changed with the set_keepalive() function.
352 : *
353 : * The flag is used to mark new connections with the SO_KEEPALIVE flag.
354 : * This is used whenever a service may take a little to long to answer
355 : * and avoid losing the TCP connection before the answer is sent to
356 : * the client.
357 : *
358 : * \warning
359 : * It is very likely that the BIO interface forces the keepalive flag
360 : * automatically so even if false here, it is very likely that your
361 : * connection will either way be marked as keepalive.
362 : *
363 : * \return The current status of the keepalive flag.
364 : */
365 0 : bool tcp_bio_server::get_keepalive() const
366 : {
367 0 : return f_impl->f_keepalive;
368 : }
369 :
370 :
371 : /** \brief Set the keepalive flag.
372 : *
373 : * This function sets the keepalive flag to either true (i.e. mark connection
374 : * sockets with the SO_KEEPALIVE flag) or false. The default is true (as set
375 : * in the constructor,) because in most cases this is a feature people want.
376 : *
377 : * \warning
378 : * The keepalive flag is likely force within the BIO interface, so setting it
379 : * to true here (which is the default anyway) probably has no real effect
380 : * (i.e. the fact is set a second time).
381 : *
382 : * \param[in] yes Whether to keep new connections alive even when no traffic
383 : * goes through.
384 : */
385 0 : void tcp_bio_server::set_keepalive(bool yes)
386 : {
387 0 : f_impl->f_keepalive = yes;
388 0 : }
389 :
390 :
391 : /** \brief Return the current status of the close_on_exec flag.
392 : *
393 : * This function returns the current status of the close_on_exec flag. This
394 : * flag is set to false by default (in the constructor.) It can be
395 : * changed with the set_close_on_exec() function.
396 : *
397 : * The flag is used to atomically mark new connections with the FD_CLOEXEC
398 : * flag. This prevents child processes from inheriting the socket (i.e. if
399 : * you use the system() function, for example, that process would inherit
400 : * your socket).
401 : *
402 : * \return The current status of the close_on_exec flag.
403 : */
404 0 : bool tcp_bio_server::get_close_on_exec() const
405 : {
406 0 : return f_impl->f_close_on_exec;
407 : }
408 :
409 :
410 : /** \brief Set the close_on_exec flag.
411 : *
412 : * This function sets the close_on_exec flag to either true (i.e. mark connection
413 : * sockets with the FD_CLOEXEC flag) or false. The default is false (as set
414 : * in the constructor,) because in our legacy code, the flag is not expected
415 : * to be set.
416 : *
417 : * \note
418 : * When set to true, the FD_CLOEXEC is also set on the listening socket so
419 : * the child can't snatch connections from under our feet.
420 : *
421 : * \warning
422 : * This is not thread safe. The BIO_do_accept() implementation uses the
423 : * accept() function which then returns and we set the FD_CLOEXEC flag
424 : * on the socket. This means it's not secure if you use exec() in a
425 : * separate thread (i.e. it may share the socket anyway unless your accept
426 : * is protected from such things). If you need to have a separate process,
427 : * look into using a fork() instead of force close the sockets in the
428 : * child process.
429 : *
430 : * \param[in] yes Whether to close on exec() or not.
431 : */
432 0 : void tcp_bio_server::set_close_on_exec(bool yes)
433 : {
434 0 : f_impl->f_close_on_exec = yes;
435 :
436 0 : if(yes)
437 : {
438 : // retrieve the socket (we do not yet have a bio_client object
439 : // so we cannot call a get_socket() function...)
440 : //
441 0 : int socket(-1);
442 : #pragma GCC diagnostic push
443 : #pragma GCC diagnostic ignored "-Wold-style-cast"
444 0 : BIO_get_fd(f_impl->f_listen.get(), &socket);
445 : #pragma GCC diagnostic pop
446 0 : if(socket >= 0)
447 : {
448 : // if this call fails, we ignore the error, but still log the event
449 : //
450 0 : if(fcntl(socket, F_SETFD, FD_CLOEXEC) != 0)
451 : {
452 0 : int const e(errno);
453 0 : SNAP_LOG_WARNING
454 : << "tcp_bio_server::set_close_on_exec(): an error occurred trying"
455 0 : " to mark accepted socket with FD_CLOEXEC ("
456 : << e
457 : << ", "
458 0 : << strerror(e)
459 : << ")."
460 : << SNAP_LOG_SEND;
461 : }
462 : }
463 : }
464 0 : }
465 :
466 :
467 : /** \brief Tell you whether the server uses a secure BIO or not.
468 : *
469 : * This function checks whether the BIO is using encryption (true)
470 : * or is a plain connection (false).
471 : *
472 : * \return true if the BIO was created in secure mode.
473 : */
474 0 : bool tcp_bio_server::is_secure() const
475 : {
476 0 : return f_impl->f_ssl_ctx != nullptr;
477 : }
478 :
479 :
480 : /** \brief Get the listening socket.
481 : *
482 : * This function returns the file descriptor of the listening socket.
483 : * By default the socket is in blocking mode.
484 : *
485 : * \return The listening socket file descriptor.
486 : */
487 0 : int tcp_bio_server::get_socket() const
488 : {
489 0 : if(f_impl->f_listen)
490 : {
491 0 : int c(-1);
492 : #pragma GCC diagnostic push
493 : #pragma GCC diagnostic ignored "-Wold-style-cast"
494 0 : BIO_get_fd(f_impl->f_listen.get(), &c);
495 : #pragma GCC diagnostic pop
496 0 : return c;
497 : }
498 :
499 0 : return -1;
500 : }
501 :
502 :
503 : /** \brief Retrieve one new connection.
504 : *
505 : * This function will wait until a new connection arrives and returns a
506 : * new bio_client object for each new connection.
507 : *
508 : * If the socket is made non-blocking then the function may return without
509 : * a bio_client object (i.e. a null pointer instead.)
510 : *
511 : * \return A shared pointer to a newly allocated bio_client object.
512 : */
513 0 : tcp_bio_client::pointer_t tcp_bio_server::accept()
514 : {
515 : // TBD: does one call to BIO_do_accept() accept at most one connection
516 : // at a time or could it be that 'r' will be set to 2, 3, 4...
517 : // as more connections get accepted?
518 : //
519 0 : int const r(BIO_do_accept(f_impl->f_listen.get()));
520 0 : if(r <= 0)
521 : {
522 : // TBD: should we instead return an empty shared pointer in this case?
523 : //
524 0 : detail::bio_log_errors();
525 0 : throw event_dispatcher_runtime_error("failed accepting a new BIO client");
526 : }
527 :
528 : // retrieve the new connection by "popping it"
529 : //
530 0 : std::shared_ptr<BIO> bio; // use reset(), see SNAP-507
531 0 : bio.reset(BIO_pop(f_impl->f_listen.get()), detail::bio_deleter);
532 0 : if(bio == nullptr)
533 : {
534 0 : detail::bio_log_errors();
535 0 : throw event_dispatcher_runtime_error("failed retrieving the accepted BIO");
536 : }
537 :
538 : // mark the new connection with the SO_KEEPALIVE flag
539 : //
540 : // note that it is likely that the BIO implementation already does that
541 : // automatically once the accept() succeeded.
542 : //
543 0 : if(f_impl->f_keepalive)
544 : {
545 : // retrieve the socket (we do not yet have a bio_client object
546 : // so we cannot call a get_socket() function...)
547 : //
548 0 : int socket(-1);
549 : #pragma GCC diagnostic push
550 : #pragma GCC diagnostic ignored "-Wold-style-cast"
551 0 : BIO_get_fd(bio.get(), &socket);
552 : #pragma GCC diagnostic pop
553 0 : if(socket >= 0)
554 : {
555 : // if this call fails, we ignore the error, but still log the event
556 : //
557 0 : int optval(1);
558 0 : socklen_t const optlen(sizeof(optval));
559 0 : if(setsockopt(socket, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) != 0)
560 : {
561 0 : SNAP_LOG_WARNING
562 : << "tcp_bio_server::accept(): an error occurred trying"
563 0 : " to mark accepted socket with SO_KEEPALIVE."
564 : << SNAP_LOG_SEND;
565 : }
566 : }
567 : }
568 :
569 : // force a close on exec() to avoid sharing the socket in child processes
570 0 : if(f_impl->f_close_on_exec)
571 : {
572 : // retrieve the socket (we do not yet have a bio_client object
573 : // so we cannot call a get_socket() function...)
574 : //
575 0 : int socket(-1);
576 : #pragma GCC diagnostic push
577 : #pragma GCC diagnostic ignored "-Wold-style-cast"
578 0 : BIO_get_fd(bio.get(), &socket);
579 : #pragma GCC diagnostic pop
580 0 : if(socket >= 0)
581 : {
582 : // if this call fails, we ignore the error, but still log the event
583 : //
584 0 : if(fcntl(socket, F_SETFD, FD_CLOEXEC) != 0)
585 : {
586 0 : SNAP_LOG_WARNING
587 : << "tcp_bio_server::accept(): an error occurred trying"
588 0 : " to mark accepted socket with FD_CLOEXEC."
589 : << SNAP_LOG_SEND;
590 : }
591 : }
592 : }
593 :
594 : // tcp_bio_client is private so we can't use the std::make_shared<>
595 : //
596 0 : tcp_bio_client::pointer_t client(new tcp_bio_client);
597 :
598 0 : client->f_impl->f_bio = bio;
599 :
600 : // TODO: somehow this does not seem to give us any information
601 : // about the cipher and other details...
602 : //
603 : // this is because it is (way) too early, we did not even
604 : // receive the HELLO yet!
605 : //
606 0 : SSL * ssl(nullptr);
607 : #pragma GCC diagnostic push
608 : #pragma GCC diagnostic ignored "-Wold-style-cast"
609 0 : BIO_get_ssl(bio.get(), &ssl);
610 : #pragma GCC diagnostic pop
611 0 : if(ssl != nullptr)
612 : {
613 0 : char const * cipher_name(SSL_get_cipher(ssl));
614 0 : int cipher_bits(0);
615 0 : SSL_get_cipher_bits(ssl, &cipher_bits);
616 0 : SNAP_LOG_DEBUG
617 0 : << "accepted BIO client with SSL cipher \""
618 : << cipher_name
619 0 : << "\" representing "
620 : << cipher_bits
621 : << " bits of encryption."
622 : << SNAP_LOG_SEND;
623 : }
624 :
625 0 : return client;
626 : }
627 :
628 :
629 :
630 6 : } // namespace ed
631 : // vim: ts=4 sw=4 et
|