CPPMyth
Library to interoperate with MythTV server
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 #include "cppdef.h"
25 
26 #include <errno.h>
27 
28 #ifdef __WINDOWS__
29 #include <WinSock2.h>
30 #define LASTERROR WSAGetLastError()
31 #define ERRNO_INTR WSAEINTR
32 #else
33 #define LASTERROR errno
34 #define ERRNO_INTR EINTR
35 #endif /* __WINDOWS__ */
36 
37 using namespace NSROOT;
38 
39 SSLSessionFactory* SSLSessionFactory::m_instance = 0;
40 
41 SSLSessionFactory& SSLSessionFactory::Instance()
42 {
43  if (!m_instance)
44  m_instance = new SSLSessionFactory();
45  return *m_instance;
46 }
47 
48 void SSLSessionFactory::Destroy()
49 {
50  SAFE_DELETE(m_instance);
51 }
52 
53 #if HAVE_OPENSSL
54 
55 #include <openssl/ssl.h>
56 #include <openssl/err.h>
57 #include <openssl/pem.h>
58 #include <openssl/x509.h>
59 #include <openssl/x509_vfy.h>
60 
61 /* Cipher suites, https://www.openssl.org/docs/apps/ciphers.html */
62 const char* const PREFERRED_CIPHERS = "HIGH:!aNULL:!kRSA:!SRP:!PSK:!CAMELLIA:!RC4:!MD5:!DSS";
63 
64 SSLSessionFactory::SSLSessionFactory()
65 : m_enabled(false)
66 , m_ctx(NULL)
67 {
68  if (SSL_library_init() < 0)
69  DBG(DBG_ERROR, "%s: could not initialize the SSL library\n", __FUNCTION__);
70  else
71  {
72  SSL_load_error_strings();
73  /* SSL_load_error_strings loads both libssl and libcrypto strings */
74  /* ERR_load_crypto_strings(); */
75 
76 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
77  m_ctx = SSL_CTX_new(TLS_client_method());
78 #else
79  m_ctx = SSL_CTX_new(SSLv23_client_method());
80 #endif
81  if (m_ctx == NULL)
82  DBG(DBG_ERROR, "%s: could not create the SSL context\n", __FUNCTION__);
83  else
84  {
85  SSL_CTX_set_verify(static_cast<SSL_CTX*>(m_ctx), SSL_VERIFY_NONE, 0);
86 
87  /* Remove the most egregious. Because SSLv2 and SSLv3 have been removed,
88  * a TLSv1.0 handshake is used. The client accepts TLSv1.0 and above.
89  * An added benefit of TLS 1.0 and above are TLS extensions like Server
90  * Name Indicatior (SNI).
91  */
92  const long flags = SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
93  (void)SSL_CTX_set_options(static_cast<SSL_CTX*>(m_ctx), flags);
94 
95  /* Each cipher suite takes 2 bytes in the ClientHello, so advertising every
96  * cipher suite available at the client is going to cause a big ClientHello
97  * (or bigger then needed to get the job done).
98  * When using SSL_CTX_set_cipher_list or SSL_set_cipher_list with the string
99  * below you'll cut the number of cipher suites down to about 45.
100  */
101  if (SSL_CTX_set_cipher_list(static_cast<SSL_CTX*>(m_ctx), PREFERRED_CIPHERS) != 1)
102  DBG(DBG_ERROR, "%s: Set cipher list failed\n", __FUNCTION__);
103 
104  /* The SSL trace callback is only used for verbose logging */
105  /* SSL_CTX_set_msg_callback(static_cast<SSL_CTX*>(m_ctx), ssl_trace); */
106 
107  DBG(DBG_INFO, "%s: SSL engine initialized\n", __FUNCTION__);
108  m_enabled = true;
109  }
110  }
111 }
112 
113 SSLSessionFactory::~SSLSessionFactory()
114 {
115  if (m_ctx)
116  SSL_CTX_free(static_cast<SSL_CTX*>(m_ctx));
117  ERR_free_strings();
118  EVP_cleanup();
119  DBG(DBG_INFO, "%s: SSL resources destroyed\n", __FUNCTION__);
120 }
121 
122 SecureSocket* SSLSessionFactory::NewSocket()
123 {
124  if (m_enabled)
125  {
126  SSL* ssl = SSL_new(static_cast<SSL_CTX*>(m_ctx));
127  /* SSL_MODE_AUTO_RETRY
128  * With this option set, if the server suddenly wants a new handshake,
129  * OpenSSL handles it in the background. Without this option, any read
130  * or write operation will return an error if the server wants a new
131  * handshake, setting the retry flag in the process.
132  */
133  SSL_set_mode(static_cast<SSL*>(ssl), SSL_MODE_AUTO_RETRY);
134  return new SecureSocket(ssl);
135  }
136  return NULL;
137 }
138 
139 SecureSocket::SecureSocket(void* ssl)
140 : TcpSocket()
141 , m_ssl(ssl)
142 , m_cert(NULL)
143 , m_connected(false)
144 , m_ssl_error(0)
145 {
146 }
147 
148 SecureSocket::~SecureSocket()
149 {
150  Disconnect();
151  SSL_free(static_cast<SSL*>(m_ssl));
152 }
153 
154 bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf)
155 {
156  m_ssl_error = 0;
157  if (m_connected)
158  Disconnect();
159 
160  /* Connect the tcp socket to the server */
161  if (!TcpSocket::Connect(server, port, rcvbuf))
162  return false;
163 
164  /* setup SSL */
165  SSL_set_fd(static_cast<SSL*>(m_ssl), m_socket);
166  SSL_set_tlsext_host_name(static_cast<SSL*>(m_ssl), server); /* fix SNI */
167 
168  /* do SSL handshake */
169  for (;;)
170  {
171  int r = SSL_connect(static_cast<SSL*>(m_ssl));
172  if (r > 0)
173  break;
174  if (r < 0)
175  {
176  int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
177  if (err == SSL_ERROR_WANT_WRITE || err == SSL_ERROR_WANT_READ)
178  {
179  DBG(DBG_DEBUG, "%s: SSL retry (%d)\n", __FUNCTION__, err);
180  continue;
181  }
182  }
183  const char* errmsg = ERR_error_string(ERR_get_error(), NULL);
184  DBG(DBG_ERROR, "%s: SSL connect failed: %s\n", __FUNCTION__, errmsg);
185  TcpSocket::Disconnect();
186  return false;
187  }
188  DBG(DBG_PROTO, "%s: SSL handshake initialized\n", __FUNCTION__);
189  m_connected = true;
190  /* check for a valid certificate */
191  std::string str("");
192  if (!IsCertificateValid(str))
193  {
194  DBG(DBG_ERROR, "%s: could not get a valid certificate from the server\n", __FUNCTION__);
195  Disconnect();
196  }
197  DBG(DBG_PROTO, "%s: %s\n", __FUNCTION__, str.c_str());
198  return true;
199 }
200 
201 size_t SecureSocket::ReceiveData(void* buf, size_t n)
202 {
203  if (m_connected && n > 0)
204  {
205  m_ssl_error = SSL_ERROR_NONE;
206  for (;;)
207  {
208  if (SSL_pending(static_cast<SSL*>(m_ssl)) == 0)
209  {
210  int hangcount = 0;
211  for (;;)
212  {
213  int s = TcpSocket::Listen(&m_timeout);
214  if (s > 0)
215  break;
216  else if (s == 0)
217  {
218  DBG(DBG_WARN, "%s: socket(%p) timed out (%d)\n", __FUNCTION__, &m_socket, hangcount);
219  m_errno = ETIMEDOUT;
220  if (++hangcount >= m_attempt)
221  return 0;
222  }
223  else if (m_errno != ERRNO_INTR)
224  return 0;
225  }
226  }
227 
228  int r = SSL_read(static_cast<SSL*>(m_ssl), buf, (int) n);
229  if (r >= 0)
230  return (size_t) r;
231  int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
232  if (err == SSL_ERROR_WANT_READ)
233  {
234  DBG(DBG_DEBUG, "%s: SSL retry\n", __FUNCTION__);
235  continue;
236  }
237  if (err == SSL_ERROR_WANT_WRITE)
238  {
239  DBG(DBG_DEBUG, "%s: SSL wants write\n", __FUNCTION__);
240  m_ssl_error = err;
241  break;
242  }
243  const char* errmsg = ERR_error_string(ERR_get_error(), NULL);
244  DBG(DBG_ERROR, "%s: SSL read failed: %s\n", __FUNCTION__, errmsg);
245  m_ssl_error = err;
246  break;
247  }
248  }
249  return 0;
250 }
251 
252 bool SecureSocket::SendData(const char* buf, size_t size)
253 {
254  if (m_connected && size > 0)
255  {
256  m_ssl_error = SSL_ERROR_NONE;
257  for (;;)
258  {
259  int r = SSL_write(static_cast<SSL*>(m_ssl), buf, (int) size);
260  if (r > 0 && size == (size_t) r)
261  return true;
262  int err = SSL_get_error(static_cast<SSL*>(m_ssl), r);
263  if (err == SSL_ERROR_WANT_WRITE)
264  {
265  DBG(DBG_DEBUG, "%s: SSL retry\n", __FUNCTION__);
266  continue;
267  }
268  if (err == SSL_ERROR_WANT_READ)
269  {
270  DBG(DBG_DEBUG, "%s: SSL wants read\n", __FUNCTION__);
271  m_ssl_error = err;
272  break;
273  }
274  const char* errmsg = ERR_error_string(ERR_get_error(), NULL);
275  DBG(DBG_ERROR, "%s: SSL write failed: %s\n", __FUNCTION__, errmsg);
276  m_ssl_error = err;
277  break;
278  }
279  }
280  return false;
281 }
282 
283 void SecureSocket::Disconnect()
284 {
285  if (m_connected)
286  {
287  SSL_shutdown(static_cast<SSL*>(m_ssl));
288  m_connected = false;
289  }
290  TcpSocket::Disconnect();
291  if (m_cert)
292  {
293  X509_free(static_cast<X509*>(m_cert));
294  m_cert = NULL;
295  }
296 }
297 
298 bool SecureSocket::IsValid() const
299 {
300  return m_connected;
301 }
302 
303 bool SecureSocket::IsCertificateValid(std::string& str)
304 {
305  if (m_cert)
306  X509_free(static_cast<X509*>(m_cert));
307  m_cert = SSL_get_peer_certificate(static_cast<SSL*>(m_ssl));
308  if (m_cert)
309  {
310  char buf[80];
311  // X509_get_subject_name() returns the subject name of certificate x.
312  // The returned value is an internal pointer which MUST NOT be freed.
313  X509_NAME* name = X509_get_subject_name(static_cast<X509*>(m_cert));
314  str.assign(X509_NAME_oneline(name, buf, sizeof(buf) - 1));
315  return true;
316  }
317  return false;
318 }
319 
320 #else
321 
322 SSLSessionFactory::SSLSessionFactory()
323 : m_enabled(false)
324 , m_ctx(NULL)
325 {
326  DBG(DBG_INFO, "%s: SSL feature is disabled\n", __FUNCTION__);
327 }
328 
329 SSLSessionFactory::~SSLSessionFactory()
330 {
331 }
332 
333 SecureSocket* SSLSessionFactory::NewSocket()
334 {
335  return new SecureSocket(NULL);
336 }
337 
338 SecureSocket::SecureSocket(void* ssl)
339 : TcpSocket()
340 , m_ssl(ssl)
341 , m_cert(NULL)
342 , m_connected(false)
343 , m_ssl_error(0)
344 {
345 }
346 
347 SecureSocket::~SecureSocket()
348 {
349 }
350 
351 bool SecureSocket::Connect(const char* server, unsigned port, int rcvbuf)
352 {
353  (void)server;
354  (void)port;
355  (void)rcvbuf;
356  return false;
357 }
358 
359 size_t SecureSocket::ReceiveData(void* buf, size_t n)
360 {
361  (void)buf;
362  (void)n;
363  return 0;
364 }
365 
366 bool SecureSocket::SendData(const char* buf, size_t size)
367 {
368  (void)buf;
369  (void)size;
370  return false;
371 }
372 
373 void SecureSocket::Disconnect()
374 {
375 }
376 
377 bool SecureSocket::IsValid() const
378 {
379  return m_connected;
380 }
381 
382 bool SecureSocket::IsCertificateValid(std::string& str)
383 {
384  (void)str;
385  return false;
386 }
387 
388 #endif
void * m_ctx
SSL default context for the application.
Definition: securesocket.h:49
bool m_enabled
SSL feature status.
Definition: securesocket.h:48