RDK Documentation (Open Sourced RDK Components)
tcpOpensslProxyServer.cpp
1 #include <arpa/inet.h>
2 #include <openssl/ssl.h>
3 #include <openssl/err.h>
4 #include <netinet/in.h>
5 #include <cerrno>
6 #include <cstring>
7 #include <signal.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <stdbool.h>
13 #include <fcntl.h>
14 
15 #include <poll.h>
16 #include <vector>
17 #include <unordered_map>
18 
19 #include <openssl/rand.h>
20 #include <openssl/x509v3.h>
21 #include <openssl/pkcs12.h>
22 #include <openssl/err.h>
23 
24 
25 #include "tcpOpensslProxyServer.h"
26 #include <QThread>
27 #include <thread>
28 
29 /* Buffer size to be used for transfers */
30 #define BUFSIZE 8192
31 
32 typedef struct _connectionContext {
33  SSL *ssl;
34  tcpOpensslProxyServer* instance;
36 
37 
38 static void *connection_handler(void *socket_desc);
39 static void *clientRead_handler(void *socket_desc);
40 
41 tcpOpensslProxyServer::tcpOpensslProxyServer()
42 {
43  m_isServerprocessingEnabled = true;
44  m_serverSSlCtx = NULL;
45  m_serverSocketListenfd = 0;
46  m_instanceServer = this;
47  onMessageReceivedCallBack = NULL;
48 }
49 
50 tcpOpensslProxyServer::~tcpOpensslProxyServer()
51 {
52  m_isServerprocessingEnabled = false;
53  m_serverSSlCtx = NULL;
54  m_serverSocketListenfd = 0;
55  m_instanceServer = NULL;
56  onMessageReceivedCallBack = NULL;
57 }
58 
59 void tcpOpensslProxyServer::freeSSLContext(SSL_CTX *ctx)
60 {
61  SSL_CTX_free(ctx);
62 }
63 
64 void tcpOpensslProxyServer::closeClient(int clientId) {
65 
66  printf ("tcpOpensslProxyServer::%s %d entering client %d\n", __FUNCTION__, __LINE__, clientId);
67  if (m_clientSSLCtxList.find(clientId) != m_clientSSLCtxList.end()){
68  /* Cleanup the SSL handle will occure while thread is exiting*/
69  //Clinet context is removed to diable anymore write
70  m_clientSSLCtxList.erase (clientId);
71  }
72  if (m_clientSSLIsReadEnabledList.find(clientId) != m_clientSSLIsReadEnabledList.end()){
73  printf ("tcpOpensslProxyServer::%s %d Closing client %d\n", __FUNCTION__, __LINE__, clientId);
74  bool* isReadEnable = m_clientSSLIsReadEnabledList[clientId];
75  *isReadEnable = false;
76  m_clientSSLIsReadEnabledList.erase (clientId);
77  }
78 }
79 
80 
81 void tcpOpensslProxyServer::closeAllClients() {
82  //close all client connections
83  for (auto& it: m_clientSSLCtxList) {
84  closeClient (it.first);
85  }
86 }
87 
88 void tcpOpensslProxyServer::stopServer() {
89  m_isServerprocessingEnabled = false;
90 
91  if (m_serverSocketListenfd){
92  ::close (m_serverSocketListenfd);
93  m_serverSocketListenfd = 0;
94  }
95  if (NULL != m_serverSSlCtx){
96  freeSSLContext (m_serverSSlCtx);
97  m_serverSSlCtx = NULL;
98  }
99 
100  closeAllClients ();
101 
102  printf ("\ntcpOpensslProxyServer::%s %d exited\n", __FUNCTION__, __LINE__);
103 }
104 
105 int tcpOpensslProxyServer::setUpServer(const char* ipAddress, unsigned short port_num, const char *ca_pem,
106  const char *cert_pk12, const char *pass)
107 {
108  SSL_CTX *ctx;
109  int listen_fd;
110 
111  /* Parse the port number, and then validate it's range */
112  if (port_num < 1 || port_num > 65535) {
113  fprintf(stderr, "tcpOpensslProxyServer::%s %d Invalid port number: %d\n", __FUNCTION__, __LINE__, port_num);
114  onSocketError (QString ("Invalid port number"));
115  return -1;
116  }
117 
118  /* Initialize OpenSSL */
119  SSL_load_error_strings();
120  OpenSSL_add_ssl_algorithms();
121 
122  /* Get a server context for our use */
123  if (!(ctx = get_server_context_pk12(ca_pem, cert_pk12, pass))) {
124  return -1;
125  }
126 
127  /* Get a socket which is ready to listen on the server's port number */
128  if ((listen_fd = get_socket(ipAddress, port_num)) < 0) {
129  freeSSLContext(ctx);
130  return 0;
131  }
132 
133  m_serverSSlCtx = ctx;
134  m_serverSocketListenfd = listen_fd;
135 
136  std::thread t1(connection_handler, m_instanceServer);
137  t1.detach();
138 
139  return 0;
140 
141 }
142 
143 SSL_CTX * tcpOpensslProxyServer::get_server_context_pk12(const char *ca_pem,
144  const char *cert_pk12,
145  const char *pass) {
146 
147  SSL_CTX *ctx;
148  FILE *f;
149  PKCS12 *p12;
150  EVP_PKEY *pri;
151  X509 *x509;
152 
153  /* Get a default context */
154  //TLSv1_2_server_method is for old mips support. Newer versions will support SSLv23_server_method
155  if (!(ctx = SSL_CTX_new(TLSv1_2_server_method()))) {
156  fprintf(stderr, "tcpOpensslProxyServer::%s %d SSL_CTX_new failed\n", __FUNCTION__, __LINE__);
157  onSslErrors (QString("SSL_CTX_new failed"));
158  return NULL;
159  }
160 
161  /*Here 1 the highest preference curve is automatically used for ECDH keys used during key exchange. This is just to support the old mips morty platforms*/
162  if(!SSL_CTX_set_ecdh_auto(ctx, 1)) {
163  fprintf(stderr, "tcpOpensslProxyServer::%s %d SSL_CTX_set_ecdh_auto(ctx, 1)\n", __FUNCTION__, __LINE__);
164  onSslErrors (QString("failed to set ecdh param"));
165  freeSSLContext (ctx);
166  return NULL;
167  }
168 
169  /* Set the CA file location for the server */
170  if (SSL_CTX_load_verify_locations(ctx, ca_pem, NULL) != 1) {
171  fprintf(stderr, "tcpOpensslProxyServer::%s %d Could not set the CA file location\n", __FUNCTION__, __LINE__);
172  onSslErrors (QString("Could not set the CA file location"));
173  freeSSLContext (ctx);
174  return NULL;
175  }
176 
177  /* Load the client's CA file location as well */
178  SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(ca_pem));
179 
180  f = fopen(cert_pk12,"rb");
181  if (!f) {
182  fprintf(stderr, "tcpOpensslProxyServer::could not open PKCS12 file '%s'", cert_pk12);
183  return 0;
184  }
185  p12 = d2i_PKCS12_fp(f, NULL);
186  fclose(f);
187 
188  PKCS12_PBE_add();
189 
190  if (!PKCS12_parse(p12, pass, &pri, &x509, NULL)) {
191  fprintf(stderr,
192  "tcpOpensslProxyServer::could not parse PKCS12 file, check password, OpenSSL error %s",
193  ERR_error_string(ERR_get_error(), NULL));
194  return 0;
195  }
196 
197  PKCS12_free(p12);
198 
199  if(SSL_CTX_use_certificate(ctx, x509) != 1) {
200  //fprintf(stderr, SSL_CLIENT_CERT_ERR);
201  fprintf(stderr, "tcpOpensslProxyServer::pk12 certificate error");
202  EVP_PKEY_free(pri);
203  X509_free(x509);
204  return 0;
205  }
206 
207  if(SSL_CTX_use_PrivateKey(ctx, pri) != 1) {
208  fprintf(stderr, "tcpOpensslProxyServer::unable to use private key from PKCS12 file '%s'",
209  cert_pk12);
210  EVP_PKEY_free(pri);
211  X509_free(x509);
212  return 0;
213  }
214 
215  EVP_PKEY_free(pri);
216  X509_free(x509);
217 
218  /* We've loaded both certificate and the key, check if they match */
219  if (SSL_CTX_check_private_key(ctx) != 1) {
220  fprintf(stderr, "tcpOpensslProxyServer::%s %d Server's certificate and the key don't match\n", __FUNCTION__, __LINE__);
221  onSslErrors (QString("Server's certificate and the key don't match"));
222  freeSSLContext (ctx);
223  return NULL;
224  }
225 
226  //mTLS happens here. Server verify client as well.
227  bool is_mTLS = true;
228  if (is_mTLS) {
229  /* We won't handle incomplete read/writes due to renegotiation */
230  SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
231 
232  /* Specify that we need to verify the client as well */
233  SSL_CTX_set_verify(ctx,
234  SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
235  NULL);
236 
237  /* We accept only certificates signed only by the CA himself */
238  SSL_CTX_set_verify_depth(ctx, 2);
239  }
240 
241  /* Done, return the context */
242  return ctx;
243 
244 }
245 
246 
247 bool tcpOpensslProxyServer::SetSocketBlockingEnabled(int fd, bool blocking)
248 {
249  if (fd < 0) return false;
250  #ifdef WIN32
251  unsigned long mode = blocking ? 0 : 1;
252  return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? TRUE : FALSE;
253  #else
254  int flags = fcntl(fd, F_GETFL, 0);
255  if (flags < 0) return false;
256  flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
257  return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
258  #endif
259 }
260 
261 int tcpOpensslProxyServer::get_socket(const char* ipAddress, unsigned short port_num) {
262 
263  struct sockaddr_in sin;
264  int sock, val;
265 
266  /* Create a socket */
267  if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
268  fprintf(stderr, "tcpOpensslProxyServer::%s %d Cannot create a socket\n", __FUNCTION__, __LINE__);
269  onSocketError (QString ("Cannot create a socket"));
270  return -1;
271  }
272 
273  if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) {
274  fprintf(stderr, "tcpOpensslProxyServer::%s %d Could not set SO_REUSEADDR on the socket\n", __FUNCTION__, __LINE__);
275  onSocketError (QString ("Could not set SO_REUSEADDR on the socket"));
276  ::close(sock); return -1;
277  }
278 
279  /* Fill up the server's socket structure */
280  memset(&sin, 0, sizeof(sin));
281  sin.sin_family = AF_INET;
282  sin.sin_addr.s_addr = inet_addr(ipAddress);
283  sin.sin_port = htons(port_num);
284 
285 
286  /* Bind the socket to the specified port number */
287  if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
288  fprintf(stderr, "tcpOpensslProxyServer::%s %d Could not bind the socket\n", __FUNCTION__, __LINE__);
289  onSocketError (QString ("Could not bind the socket"));
290  ::close(sock); return -1;
291  }
292 
293  /* Specify that this is a listener socket */
294  if (listen(sock, SOMAXCONN) < 0) {
295  fprintf(stderr, "tcpOpensslProxyServer::%s %d Failed to listen on this socket\n", __FUNCTION__, __LINE__);
296  onSocketError (QString ("Failed to listen on this socket"));
297  ::close(sock); return -1;
298  }
299 
300  /* Done, return the socket */
301  return sock;
302 
303 }
304 
305 
306 void *connection_handler(void *instance){
307  struct sockaddr_in sin;
308  socklen_t sin_len;
309  SSL *ssl;
310  int net_fd;
311 
312  tcpOpensslProxyServer* instanceServer = (tcpOpensslProxyServer*)instance;
313 
314  SSL_CTX *ctx = instanceServer->m_serverSSlCtx;
315  int listen_fd = instanceServer->m_serverSocketListenfd;
316 
317 
318  struct timeval tv;
319  tv.tv_sec = 20; //second
320  setsockopt(listen_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
321  fprintf(stderr, "tcpOpensslProxyServer::%s %d Server waiting to accept connection\n", __FUNCTION__, __LINE__);
322  /* Get to work */
323  while (instanceServer->m_isServerprocessingEnabled) {
324  /* Hold on till we can an incoming connection */
325  sin_len = sizeof(sin);
326  net_fd = ::accept(listen_fd, (struct sockaddr *) &sin, &sin_len);
327  if (net_fd < 0) {
328  int err = errno;
329  //strerror(errno); error message.
330  if (EAGAIN != err) {
331  instanceServer->onAcceptError (QString("Failed to accept connection"));
332  fprintf(stderr, "tcpOpensslProxyServer::%s %d Failed to accept connection net_fd:%d err:%d %s\n", __FUNCTION__, __LINE__, net_fd, err, strerror(errno));
333  }
334  continue;
335  }
336 
337  /* Get an SSL handle from the context */
338  if (!(ssl = SSL_new(ctx))) {
339  fprintf(stderr, "tcpOpensslProxyServer::%s %d Could not get an SSL handle from the context\n", __FUNCTION__, __LINE__);
340  instanceServer->onSslErrors (QString("Could not get an SSL handle from the context"));
341  ::close(net_fd); //return -1;
342  }
343 
344  /* Associate the newly accepted connection with this handle */
345  SSL_set_fd(ssl, net_fd);
346 
347 
348  instanceServer->onNewConnection (net_fd, instanceServer);
349 
351  cc.ssl = ssl;
352  cc.instance = (tcpOpensslProxyServer*)instance;
353  std::thread t1(clientRead_handler, &cc);
354  t1.detach();
355 
356  }
357  printf ("tcpOpensslProxyServer::%s %d server thread exited\n", __FUNCTION__, __LINE__);
358  pthread_exit(NULL);
359 }
360 
361 void *clientRead_handler(void *socket_desc)
362 {
363 
364  static char buffer[BUFSIZE];
365  int rc, len;
366  bool isReadEnabled = true;
367 
368  printf("\ntcpOpensslProxyServer::%s:%d\n", __FUNCTION__, __LINE__);
369  connectionContext *cc = (connectionContext*) socket_desc;
370  SSL *ssl = cc->ssl;
371  int net_fd = SSL_get_fd(ssl);
372  tcpOpensslProxyServer* instance = cc->instance;
373  //Client siganl condition to unlock.
374 
375 
376  /* Now perform handshake */
377  if ((rc = SSL_accept(ssl)) != 1) {
378  fprintf(stderr, "tcpOpensslProxyServer::%s %d Could not perform SSL handshake\n", __FUNCTION__, __LINE__);
379  instance->onPeerVerifyError ("Could not perform SSL handshake");
380  if (rc != 0) {
381  SSL_shutdown(ssl);
382  }
383  SSL_free(ssl);
384  pthread_exit(NULL);
385  }
386 
387  //Write context is only required after ssl hand shake is done.
388  instance->m_clientSSLCtxList [net_fd] = ssl;
389  instance->onConnected (net_fd, QString("connected")); //need to check how to get connection ip string here
390 
391  /* Print success connection message on the server */
392  printf("tcpOpensslProxyServer::%s %d SSL handshake successful\n", __FUNCTION__, __LINE__);
393 
394 
395  instance->m_clientSSLIsReadEnabledList [net_fd] = &isReadEnabled;
396  struct timeval tv;
397  tv.tv_sec = 20; //second
398  tv.tv_usec = 0; //micro sec
399  setsockopt(net_fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
400 
401  len = 0;
402  memset (buffer, '\0', BUFSIZE);
403  while (isReadEnabled) {
404  len = SSL_read(ssl, buffer, BUFSIZE);
405 
406  if (len < 0) {
407  //SSL read timeout. Continue here. if server is stopped isReadEnabled will be zero
408  //else it will go to next read operation
409  continue;
410  } else if (0 == len) {
411  printf("\ntcpOpensslProxyServer::%s:%d debug socket read data len:%d. Closing the socket\n", __FUNCTION__, __LINE__, len);
412  //This means client disconnected. call client stop here.
413  instance->closeClient(net_fd);
414  }else {
415  //Emit message received only if read enabled for the client.
416  if (isReadEnabled) {
417  //send the message if it is not pong.
418  bool isPong = false;
419  if (len>7){
420  if (0 == strncmp(buffer, "##PING##", 8)){
421  //PING received will go to on pong
422  instance->onPong (net_fd, buffer+8, len-8);
423  isPong = true;
424  }
425  }
426  if (!isPong){
427  printf("tcpOpensslProxyServer::%s:%d received: actual data is masked len:%d", __FUNCTION__, __LINE__, len);
428  instance->onMessageReceived (net_fd, buffer, len);
429  }
430  }
431  }
432  memset (buffer, '\0', BUFSIZE);
433  }
434  printf("\ntcpOpensslProxyServer::%s %d client:%d read exited\n", __FUNCTION__, __LINE__, net_fd);
435 
436  instance->onDisconnected (net_fd);
437  instance->m_clientSSLCtxList.erase (net_fd);
438 
439 
440  /* Cleanup the SSL handle */
441  SSL_shutdown(ssl);
442  SSL_free(ssl);
443  ::close(net_fd);
444  instance->closeClient (net_fd);
445 
446  printf ("\ntcpOpensslProxyServer::%s %d thread exited len:%d\n", __FUNCTION__, __LINE__, len);
447  pthread_exit(NULL);
448 
449 }
450 
451 void tcpOpensslProxyServer::sendTextMessage (int clientId, const char* buffer, int len){
452 
453  int rc = 0;
454  if (m_clientSSLCtxList.find(clientId) != m_clientSSLCtxList.end()){
455  SSL *ssl = m_clientSSLCtxList[clientId];
456  if ((rc = SSL_write(ssl, buffer, len)) != len){
457  fprintf(stderr, "tcpOpensslProxyServer::%s %d SSL write failed\n", __FUNCTION__, __LINE__);
458  }
459 
460  }
461  else {
462  printf ("\ntcpOpensslProxyServer::%s %d Not able to signal the write thread operation for the client: %d\n", __FUNCTION__, __LINE__, clientId);
463  }
464 
465 }
466 
467 void tcpOpensslProxyServer::setPingMsg (int clientId, const char* uniqueId, int len){
468  std::string s = "##PING##";
469  s.append (uniqueId, len);
470  m_clientPingMsgList [clientId] = s;
471 }
472 void tcpOpensslProxyServer::ping (int clientId){
473  std::string s = "##PING##DUMMY";
474  if (m_clientPingMsgList.find(clientId) != m_clientPingMsgList.end()){
475  s = m_clientPingMsgList [clientId];
476  }
477  sendTextMessage (clientId, s.c_str(), strlen(s.c_str()));
478  std::chrono::high_resolution_clock::time_point pingTime = std::chrono::high_resolution_clock::now();
479  m_clientPingTimeList [clientId] = pingTime;
480 }
481 
482 void tcpOpensslProxyServer::onPong (int clientId, char* msg, int len){
483  setPingMsg (clientId, msg, len);
484  QByteArray databuf = QByteArray((char*)msg, len);
485  std::chrono::high_resolution_clock::time_point pingTime;
486  if (m_clientPingTimeList.find(clientId) != m_clientPingTimeList.end()){
487  pingTime = m_clientPingTimeList [clientId];
488  } else {
489  pingTime = std::chrono::high_resolution_clock::now();
490  }
491  std::chrono::high_resolution_clock::time_point pongTime = std::chrono::high_resolution_clock::now();
492  std::uint64_t ticks = (std::chrono::duration_cast<std::chrono::milliseconds>(pongTime - pingTime)).count();
493  if (NULL != onPongCallBack){
494  onPongCallBack (clientId, (quint64)ticks, databuf);
495  } else {
496  fprintf(stderr, "tcpOpensslProxyServer::%s:%d onPongCallBack NULL\n",__FUNCTION__, __LINE__);
497  }
498 }
499 
500 void tcpOpensslProxyServer::onNewConnection (int clientId, tcpOpensslProxyServer* ss){
501  emit newConnection (clientId, ss);
502 }
503 
504 void tcpOpensslProxyServer::onConnected (int clientId, QString msg){
505  emit connected (clientId, msg);
506 }
507 
508 void tcpOpensslProxyServer::onMessageReceived (int clientId, char* message, size_t len){
509  if (NULL != onMessageReceivedCallBack){
510  onMessageReceivedCallBack (clientId, message, len);
511  }
512 }
513 
514 void tcpOpensslProxyServer::onDisconnected (int clientId){
515  emit disconnected (clientId);
516 }
517 
518 void tcpOpensslProxyServer::onSocketError (QString err){
519  emit socketError (err);
520 }
521 
522 void tcpOpensslProxyServer::onSslErrors (QString err) {
523  emit sslErrors (err);
524 }
525 
526 void tcpOpensslProxyServer::onAcceptError (QString err){
527  emit acceptError (err);
528 }
529 
530 void tcpOpensslProxyServer::onPeerVerifyError (QString err){
531  emit peerVerifyError (err);
532 }
tcpOpensslProxyServer
Definition: tcpOpensslProxyServer.h:15
_connectionContext
Definition: tcpOpensslProxyServer.cpp:32
TRUE
#define TRUE
Defines for TRUE/FALSE/ENABLE flags.
Definition: wifi_common_hal.h:199