CPPMyth
Library to interoperate with MythTV server
Loading...
Searching...
No Matches
securesocket.cpp
1/*
2 * Copyright (C) 2016 Jean-Luc Barriere
3 *
4 * This library is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published
6 * by the Free Software Foundation; either version 3, or (at your option)
7 * any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library; see the file COPYING. If not, write to
16 * the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
17 * MA 02110-1301 USA
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22#include "securesocket.h"
23#include "debug.h"
24
25#include <errno.h>
26
27#ifdef __WINDOWS__
28#include <WinSock2.h>
29#define LASTERROR WSAGetLastError()
30#define ERRNO_INTR WSAEINTR
31#else
32#define LASTERROR errno
33#define ERRNO_INTR EINTR
34#endif /* __WINDOWS__ */
35
36using namespace NSROOT;
37
38SSLSessionFactory* SSLSessionFactory::m_instance = 0;
39
40SSLSessionFactory& SSLSessionFactory::Instance()
41{
42 if (!m_instance)
43 m_instance = new SSLSessionFactory();
44 return *m_instance;
45}
46
47void SSLSessionFactory::Destroy()
48{
49 if (m_instance)
50 delete m_instance;
51 m_instance = nullptr;
52}
53
54#if HAVE_OPENSSL
55
56#include <openssl/ssl.h>
57#include <openssl/err.h>
58#include <openssl/pem.h>
59#include <openssl/x509.h>
60#include <openssl/x509_vfy.h>
61
62/* Cipher suites, https://www.openssl.org/docs/apps/ciphers.html */
63const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!PSK:!SRP:!MD5:!RC4:!CAMELLIA:!DSS";
64
65SSLSessionFactory::SSLSessionFactory()
66: m_client_ctx(nullptr)
67, m_enabled(false)
68{
69 if (SSL_library_init() < 0)
70 DBG(DBG_ERROR, "%s: Could not initialize the SSL library\n", __FUNCTION__);
71 else
72 {
73 SSL_load_error_strings();
74 /* SSL_load_error_strings loads both libssl and libcrypto strings */
75 /* ERR_load_crypto_strings(); */
76
77 /* Setup the default client context */
78#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
79 SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
80#else
81 SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
82#endif
83 if (!ctx)
84 DBG(DBG_ERROR, "%s: Could not create the SSL client context\n", __FUNCTION__);
85 else
86 {
87 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
88
89 /* Remove the most egregious. Because SSLv2 and SSLv3 have been removed,
90 * a TLSv1.0 handshake is used. The client accepts TLSv1.0 and above.
91 * An added benefit of TLS 1.0 and above are TLS extensions like Server
92 * Name Indicatior (SNI).
93 */
94 const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
95 (void)SSL_CTX_set_options(ctx, flags);
96
97 /* Each cipher suite takes 2 bytes in the ClientHello, so advertising every
98 * cipher suite available at the client is going to cause a big ClientHello
99 * (or bigger then needed to get the job done).
100 * When using SSL_CTX_set_cipher_list or SSL_set_cipher_list with the string
101 * below you'll cut the number of cipher suites down to about 45.
102 */
103 if (SSL_CTX_set_cipher_list(ctx, PREFERRED_CIPHERS) != 1)
104 DBG(DBG_ERROR, "%s: Set cipher list failed\n", __FUNCTION__);
105
106 /* The SSL trace callback is only used for verbose logging */
107 /* SSL_CTX_set_msg_callback(ctx, ssl_trace); */
108
109 m_client_ctx = ctx;
110 m_enabled = true;
111 DBG(DBG_DEBUG, "%s: SSL has been initialized\n", __FUNCTION__);
112 }
113 }
114}
115
116SSLSessionFactory::~SSLSessionFactory()
117{
118 if (m_client_ctx)
119 SSL_CTX_free(static_cast<SSL_CTX*>(m_client_ctx));
120 ERR_free_strings();
121 EVP_cleanup();
122 DBG(DBG_INFO, "%s: SSL resources destroyed\n", __FUNCTION__);
123}
124
126{
127 if (!m_client_ctx)
128 return nullptr;
129 SSL* ssl = SSL_new(static_cast<SSL_CTX*>(m_client_ctx));
130 /* SSL_MODE_AUTO_RETRY
131 * With this option set, if the server suddenly wants a new handshake,
132 * OpenSSL handles it in the background. Without this option, any read
133 * or write operation will return an error if the server wants a new
134 * handshake, setting the retry flag in the process.
135 */
136 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
137 return new SecureSocket(ssl);
138}
139
140SSLServerContext::~SSLServerContext()
141{
142 if (m_server_ctx)
143 {
144 DBG(DBG_DEBUG, "%s: Free SSL server context (%p)\n", __FUNCTION__, m_server_ctx);
145 SSL_CTX_free(static_cast<SSL_CTX*>(m_server_ctx));
146 }
147}
148
149bool SSLServerContext::InitContext(const std::string& certfile, const std::string& pkeyfile)
150{
151 if (m_server_ctx || !SSLSessionFactory::Instance().IsEnabled())
152 return false;
153
154 /* Setup server context */
155#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
156 SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
157#else
158 SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
159#endif
160 if (!ctx)
161 {
162 DBG(DBG_ERROR, "%s: Could not create the SSL server context\n", __FUNCTION__);
163 return false;
164 }
165
166 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, 0);
167
168 /* Remove the most egregious */
169 const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
170 (void)SSL_CTX_set_options(ctx, flags);
171
172 if (SSL_CTX_set_cipher_list(ctx, "ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2") != 1)
173 DBG(DBG_ERROR, "%s: Set cipher list failed\n", __FUNCTION__);
174
175 /* The SSL trace callback is only used for verbose logging */
176 /* SSL_CTX_set_msg_callback(ctx, ssl_trace); */
177
178 /* Set the certificate to be used */
179 if (SSL_CTX_use_certificate_chain_file(ctx, certfile.c_str()) != 1)
180 {
181 DBG(DBG_ERROR, "%s: Certificate file is invalid\n", __FUNCTION__);
182 SSL_CTX_free(ctx);
183 return false;
184 }
185 /* Set the private key to be used */
186 if (SSL_CTX_use_PrivateKey_file(ctx, pkeyfile.c_str(), SSL_FILETYPE_PEM) != 1)
187 {
188 DBG(DBG_ERROR, "%s: Private key file is invalid\n", __FUNCTION__);
189 SSL_CTX_free(ctx);
190 return false;
191 }
192 /* Make sure the key and certificate file match */
193 if (SSL_CTX_check_private_key(ctx) != 1) {
194 DBG(DBG_ERROR, "%s: Private key does not match the certificate public key\n", __FUNCTION__);
195 SSL_CTX_free(ctx);
196 return false;
197 }
198 DBG(DBG_INFO, "%s: Server certificate was successfully loaded\n", __FUNCTION__);
199 m_server_ctx = ctx;
200 return true;
201}
202
204{
205 if (!m_server_ctx)
206 return nullptr;
207 SSL* ssl = SSL_new(static_cast<SSL_CTX*>(m_server_ctx));
208 /* SSL_MODE_AUTO_RETRY
209 * With this option set, if the server suddenly wants a new handshake,
210 * OpenSSL handles it in the background. Without this option, any read
211 * or write operation will return an error if the server wants a new
212 * handshake, setting the retry flag in the process.
213 */
214 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
215 return new SecureSocket(ssl);
216}
217
218TcpServerSocket::AcceptStatus SSLServerContext::SSLHandshake(SecureSocket& socket)
219{
220 SSL_set_fd(static_cast<SSL*>(socket.m_ssl), socket.m_socket);
221 SSL_set_accept_state(static_cast<SSL*>(socket.m_ssl));
222
223 /* do SSL handshake */
224 int r = SSL_accept(static_cast<SSL*>(socket.m_ssl));
225 if (r < 1)
226 {
227 socket.m_ssl_error = ERR_get_error();
228 return TcpServerSocket::ACCEPT_FAILURE;
229 }
230 DBG(DBG_PROTO, "%s: SSL handshake initialized\n", __FUNCTION__);
231 socket.m_connected = true;
232 return TcpServerSocket::ACCEPT_SUCCESS;
233}
234
235SecureSocket::SecureSocket(void* ssl)
236: TcpSocket()
237, m_ssl(ssl)
238, m_cert(nullptr)
239, m_connected(false)
240, m_ssl_error(0)
241, m_errmsg(nullptr)
242{
243}
244
245SecureSocket::~SecureSocket()
246{
248 SSL_free(static_cast<SSL*>(m_ssl));
249 if (m_errmsg)
250 delete [] m_errmsg;
251}
252
253bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf)
254{
255 m_ssl_error = 0;
256 if (m_connected)
257 Disconnect();
258
259 /* Connect the tcp socket to the server */
260 if (!TcpSocket::Connect(server, port, rcvbuf))
261 return false;
262
263 /* setup SSL */
264 SSL_set_fd(static_cast<SSL*>(m_ssl), m_socket);
265 SSL_set_tlsext_host_name(static_cast<SSL*>(m_ssl), server); /* fix SNI */
266
267 /* do SSL handshake */
268 for (;;)
269 {
270 int r = SSL_connect(static_cast<SSL*>(m_ssl));
271 if (r > 0)
272 break;
273 if (r < 0)
274 {
275 int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
276 if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ)
277 {
278 DBG(DBG_DEBUG, "%s: SSL retry (%d)\n", __FUNCTION__, err);
279 continue;
280 }
281 }
282 m_ssl_error = ERR_get_error();
283 DBG(DBG_ERROR, "%s: SSL connect failed: %s\n", __FUNCTION__, GetSSLError());
285 return false;
286 }
287 DBG(DBG_PROTO, "%s: SSL handshake initialized\n", __FUNCTION__);
288 m_connected = true;
289 /* check for a valid certificate */
290 std::string str("");
291 if (!IsCertificateValid(str))
292 {
293 DBG(DBG_ERROR, "%s: Could not get a valid certificate from the server\n", __FUNCTION__);
294 Disconnect();
295 }
296 DBG(DBG_PROTO, "%s: %s\n", __FUNCTION__, str.c_str());
297 return true;
298}
299
300size_t SecureSocket::ReceiveData(void* buf, size_t n)
301{
302 if (m_connected && n > 0)
303 {
304 m_ssl_error = SSL_ERROR_NONE;
305 for (;;)
306 {
307 if (SSL_pending(static_cast<SSL*>(m_ssl)) == 0)
308 {
309 int hangcount = 0;
310 for (;;)
311 {
312 int s = TcpSocket::Listen(&m_timeout);
313 if (s > 0)
314 break;
315 else if (s == 0)
316 {
317 DBG(DBG_INFO, "%s: socket(%p) timed out (%d)\n", __FUNCTION__, &m_socket, hangcount);
318 m_errno = ETIMEDOUT;
319 if (++hangcount >= m_attempt)
320 return 0;
321 }
322 else if (m_errno != ERRNO_INTR)
323 return 0;
324 }
325 }
326
327 int r = SSL_read(static_cast<SSL*>(m_ssl), buf, (int) n);
328 if (r >= 0)
329 return (size_t) r;
330 int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
331 if (err == SSL_ERROR_WANT_READ)
332 {
333 DBG(DBG_DEBUG, "%s: SSL retry\n", __FUNCTION__);
334 continue;
335 }
336 if (err == SSL_ERROR_WANT_WRITE)
337 {
338 DBG(DBG_DEBUG, "%s: SSL wants write\n", __FUNCTION__);
339 m_ssl_error = ERR_get_error();
340 break;
341 }
342 m_ssl_error = ERR_get_error();
343 DBG(DBG_ERROR, "%s: SSL read failed: %s\n", __FUNCTION__, GetSSLError());
344 break;
345 }
346 }
347 return 0;
348}
349
350size_t SecureSocket::BlockingRead(void* buf, size_t n)
351{
352 if (m_connected && n > 0)
353 {
354 m_ssl_error = SSL_ERROR_NONE;
355 for (;;)
356 {
357 int r = SSL_read(static_cast<SSL*>(m_ssl), buf, (int) n);
358 if (r >= 0)
359 return (size_t) r;
360 int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
361 if (err == SSL_ERROR_WANT_READ)
362 {
363 DBG(DBG_DEBUG, "%s: SSL retry\n", __FUNCTION__);
364 continue;
365 }
366 if (err == SSL_ERROR_WANT_WRITE)
367 {
368 DBG(DBG_DEBUG, "%s: SSL wants write\n", __FUNCTION__);
369 m_ssl_error = ERR_get_error();
370 break;
371 }
372 m_ssl_error = ERR_get_error();
373 DBG(DBG_ERROR, "%s: SSL read failed: %s\n", __FUNCTION__, GetSSLError());
374 break;
375 }
376 }
377 return 0;
378}
379
380bool SecureSocket::SendData(const char* buf, size_t size)
381{
382 if (m_connected && size > 0)
383 {
384 m_ssl_error = SSL_ERROR_NONE;
385 for (;;)
386 {
387 int r = SSL_write(static_cast<SSL*>(m_ssl), buf, (int) size);
388 if (r > 0 && size == (size_t) r)
389 return true;
390 int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
391 if (err == SSL_ERROR_WANT_WRITE)
392 {
393 DBG(DBG_DEBUG, "%s: SSL retry\n", __FUNCTION__);
394 continue;
395 }
396 if (err == SSL_ERROR_WANT_READ)
397 {
398 DBG(DBG_DEBUG, "%s: SSL wants read\n", __FUNCTION__);
399 m_ssl_error = ERR_get_error();
400 break;
401 }
402 m_ssl_error = ERR_get_error();
403 DBG(DBG_ERROR, "%s: SSL write failed: %s\n", __FUNCTION__, GetSSLError());
404 break;
405 }
406 }
407 return false;
408}
409
411{
412 if (m_connected)
413 {
414 SSL_shutdown(static_cast<SSL*>(m_ssl));
415 m_connected = false;
416 }
418 if (m_cert)
419 {
420 X509_free(static_cast<X509*>(m_cert));
421 m_cert = nullptr;
422 }
423}
424
425bool SecureSocket::IsCertificateValid(std::string& str)
426{
427 if (m_cert)
428 X509_free(static_cast<X509*>(m_cert));
429 m_cert = SSL_get_peer_certificate(static_cast<SSL*>(m_ssl));
430 if (m_cert)
431 {
432 char buf[80];
433 // X509_get_subject_name() returns the subject name of certificate x.
434 // The returned value is an internal pointer which MUST NOT be freed.
435 X509_NAME* name = X509_get_subject_name(static_cast<X509*>(m_cert));
436 str.assign(X509_NAME_oneline(name, buf, sizeof(buf) - 1));
437 return true;
438 }
439 return false;
440}
441
442#define ERROR_MSG_SIZE 256
443const char* SecureSocket::GetSSLError()
444{
445 // create error message buffer as needed
446 if (!m_errmsg)
447 m_errmsg = new char[ERROR_MSG_SIZE];
448 ERR_error_string_n(m_ssl_error, m_errmsg, ERROR_MSG_SIZE);
449 return m_errmsg;
450}
451
452#else
453
454SSLSessionFactory::SSLSessionFactory()
455: m_client_ctx(nullptr)
456, m_enabled(false)
457{
458 DBG(DBG_INFO, "%s: SSL feature is disabled\n", __FUNCTION__);
459}
460
461SSLSessionFactory::~SSLSessionFactory()
462{
463}
464
466{
467 return nullptr;
468}
469
470SSLServerContext::~SSLServerContext()
471{
472}
473
474bool SSLServerContext::InitContext(const std::string& certfile, const std::string& pkeyfile)
475{
476 return false;
477}
478
480{
481 return nullptr;
482}
483
484TcpServerSocket::AcceptStatus SSLServerContext::SSLHandshake(SecureSocket& socket)
485{
486 return TcpServerSocket::ACCEPT_ERROR;
487}
488
489SecureSocket::SecureSocket(void* ssl)
490: TcpSocket()
491, m_ssl(ssl)
492, m_cert(nullptr)
493, m_connected(false)
494, m_ssl_error(0)
495{
496}
497
498SecureSocket::~SecureSocket()
499{
500}
501
502bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf)
503{
504 (void)server;
505 (void)port;
506 (void)rcvbuf;
507 return false;
508}
509
510size_t SecureSocket::ReceiveData(void* buf, size_t n)
511{
512 (void)buf;
513 (void)n;
514 return 0;
515}
516
517size_t SecureSocket::BlockingRead(void* buf, size_t n)
518{
519 (void)buf;
520 (void)n;
521 return 0;
522}
523
524bool SecureSocket::SendData(const char* buf, size_t size)
525{
526 (void)buf;
527 (void)size;
528 return false;
529}
530
534
535bool SecureSocket::IsCertificateValid(std::string& str)
536{
537 (void)str;
538 return false;
539}
540
541const char* SecureSocket::GetSSLError()
542{
543 return "SSL not available";
544}
545
546#endif
SecureSocket * NewServerSocket()
void * m_server_ctx
SSL server context.
bool InitContext(const std::string &certfile, const std::string &pkeyfile)
static TcpServerSocket::AcceptStatus SSLHandshake(SecureSocket &socket)
SecureSocket * NewClientSocket()
void * m_ssl
SSL handle.
bool m_connected
SSL session state.
int m_ssl_error
SSL error code.
size_t ReceiveData(void *buf, size_t n)
bool SendData(const char *buf, size_t size)
void * m_cert
X509 certificate.
size_t BlockingRead(void *buf, size_t n)
char * m_errmsg
error message buffer
bool Connect(const char *server, unsigned port, int rcvbuf)
virtual void Disconnect()
Definition socket.cpp:503
virtual bool Connect(const char *server, unsigned port, int rcvbuf)
Definition socket.cpp:256
int Listen(timeval *timeout)
Definition socket.cpp:541