4141
4242#include <stdbool.h>
4343#include <stdlib.h>
44+ #include <assert.h>
45+ #include <signal.h>
4446
4547#include <errno.h>
4648#include <netdb.h>
5759#include <stdio.h>
5860#include <strings.h>
5961
62+ #include <openssl/ssl.h>
63+ #include <openssl/err.h>
64+
6065/**
6166 * \brief Check that a pointer is not null. Otherwise returns #SISO_ERR
6267 * \param[in] ptr Pointer to check
7176 * \brief Get the lass error message
7277 */
7378#define PERROR_LAST strerror(errno)
79+ /**
80+ * @brief Get the last global OpenSSL error message
81+ */
82+ #define TLS_LAST_ERROR tls_code_error(ERR_get_error())
83+ /**
84+ * @brief Get the last OpenSSL error message for SSL connection.
85+ */
86+ #define TLS_LAST_SSL_ERROR (ssl , ret ) tls_code_error(SSL_get_error((ssl), (ret)))
7487/** Maximum message size that can be send over UDP */
7588#define SISO_UDP_MAX 65000
7689/**
@@ -86,6 +99,7 @@ enum siso_conn_type {
8699 SC_UDP ,
87100 SC_TCP ,
88101 SC_SCTP ,
102+ SC_TLS ,
89103 SC_UNKNOWN ,
90104};
91105
@@ -109,26 +123,32 @@ static const char *siso_messages[] = {
109123static const char * siso_sc_types [] = {
110124 [SC_UDP ] = "UDP" ,
111125 [SC_TCP ] = "TCP" ,
112- [SC_SCTP ] = "SCTP"
126+ [SC_SCTP ] = "SCTP" ,
127+ [SC_TLS ] = "TLS" ,
113128};
114129
130+ /** Buffer for error messages. */
131+ static char sisco_err_msg_buf [256 ] = { 0 };
132+
115133/**
116134 * \brief Main sisolib structure
117135 */
118136struct sisoconf_s {
119137 const char * last_error ; /**< last error message */
120- enum siso_conn_type type ; /**< UDP/TCP/SCTP */
138+ enum siso_conn_type type ; /**< UDP/TCP/SCTP/TLS */
121139 struct addrinfo * servinfo ; /**< server information */
122140 int sockfd ; /**< socket descriptor */
123141 uint64_t max_speed ; /**< max sending speed */
124142 uint64_t act_speed ; /**< actual speed */
125143 struct timeval begin , end ; /**< start/end time for limited transfers */
144+ SSL_CTX * ssl_ctx ; /**< context for creating TLS connecitons */
145+ SSL * ssl ; /**< TLS connection */
126146};
127147
128148/**
129149 * \brief Constructor
130150 */
131- sisoconf * siso_create ()
151+ sisoconf * siso_create (void )
132152{
133153 /* allocate memory */
134154 sisoconf * conf = (sisoconf * ) calloc (1 , sizeof (sisoconf ));
@@ -154,6 +174,11 @@ void siso_destroy(sisoconf *conf)
154174 freeaddrinfo (conf -> servinfo );
155175 }
156176
177+ if (conf -> ssl_ctx ) {
178+ SSL_CTX_free (conf -> ssl_ctx );
179+ conf -> ssl_ctx = NULL ;
180+ }
181+
157182 free (conf );
158183}
159184
@@ -256,6 +281,7 @@ int siso_getaddrinfo(sisoconf *conf, const char *ip, const char *port)
256281 hints .ai_socktype = SOCK_DGRAM ;
257282 hints .ai_protocol = IPPROTO_UDP ;
258283 break ;
284+ case SC_TLS :
259285 case SC_TCP :
260286 hints .ai_socktype = SOCK_STREAM ;
261287 hints .ai_protocol = IPPROTO_TCP ;
@@ -281,6 +307,98 @@ int siso_getaddrinfo(sisoconf *conf, const char *ip, const char *port)
281307 return SISO_OK ;
282308}
283309
310+ /**
311+ * @brief Get OpenSSL error message for the given code
312+ */
313+ static inline const char * tls_code_error (unsigned long code )
314+ {
315+ ERR_error_string_n (code , sisco_err_msg_buf , sizeof (sisco_err_msg_buf ));
316+ return & sisco_err_msg_buf [0 ];
317+ }
318+
319+ /**
320+ * @brief Get SSL_CTX. Initialize it if it is not initialized.
321+ * @return NULL on failure.
322+ */
323+ SSL_CTX * siso_get_ssl_ctx (sisoconf * conf )
324+ {
325+ if (conf -> ssl_ctx ) {
326+ return conf -> ssl_ctx ;
327+ }
328+
329+ SSL_load_error_strings ();
330+
331+ SSL_CTX * ctx = SSL_CTX_new (TLS_client_method ());
332+ if (!ctx ) {
333+ conf -> last_error = TLS_LAST_ERROR ;
334+ return NULL ;
335+ }
336+
337+ SSL_CTX_set_verify (ctx , SSL_VERIFY_PEER , NULL );
338+
339+ if (!SSL_CTX_set_default_verify_paths (ctx )) {
340+ conf -> last_error = TLS_LAST_ERROR ;
341+ SSL_CTX_free (ctx );
342+ return NULL ;
343+ }
344+
345+ if (!SSL_CTX_set_min_proto_version (ctx , TLS1_2_VERSION )) {
346+ conf -> last_error = TLS_LAST_ERROR ;
347+ SSL_CTX_free (ctx );
348+ return NULL ;
349+ }
350+
351+ SSL_CTX_set_mode (ctx , SSL_MODE_ENABLE_PARTIAL_WRITE );
352+
353+ // There is no way to pass MSG_SIGIGN when writing using OpenSSL without
354+ // creating custom BIO. So we can just ignore SIGPIPE.
355+ signal (SIGPIPE , SIG_IGN );
356+
357+ return conf -> ssl_ctx = ctx ;
358+ }
359+
360+ int siso_tls_connect (sisoconf * conf , int new_fd )
361+ {
362+ assert (!conf -> ssl );
363+
364+ SSL_CTX * ctx = siso_get_ssl_ctx (conf );
365+ if (!ctx ) {
366+ return SISO_ERR ;
367+ }
368+
369+ SSL * ssl = SSL_new (ctx );
370+ if (!ssl ) {
371+ conf -> last_error = TLS_LAST_ERROR ;
372+ return SISO_ERR ;
373+ }
374+
375+ BIO * bio = BIO_new (BIO_s_socket ());
376+ if (!bio ) {
377+ conf -> last_error = TLS_LAST_ERROR ;
378+ SSL_free (ssl );
379+ return SISO_ERR ;
380+ }
381+
382+ BIO_set_fd (bio , new_fd , BIO_NOCLOSE );
383+ SSL_set_bio (ssl , bio , bio );
384+
385+ int res = SSL_connect (ssl );
386+ if (res <= 0 ) {
387+ // Get the best error message.
388+ long vres = SSL_get_verify_result (ssl );
389+ if (vres == X509_V_OK ) {
390+ conf -> last_error = TLS_LAST_SSL_ERROR (ssl , res );
391+ } else {
392+ conf -> last_error = X509_verify_cert_error_string (vres );
393+ }
394+ SSL_free (ssl );
395+ return SISO_ERR ;
396+ }
397+
398+ conf -> ssl = ssl ;
399+ return SISO_OK ;
400+ }
401+
284402/**
285403 * \brief Create new socket
286404 */
@@ -309,6 +427,11 @@ int siso_create_socket(sisoconf *conf)
309427 return SISO_ERR ;
310428 }
311429
430+ if (conf -> type == SC_TLS && siso_tls_connect (conf , new_fd ) != SISO_OK ) {
431+ close (new_fd );
432+ return SISO_ERR ;
433+ }
434+
312435 conf -> sockfd = new_fd ;
313436 return SISO_OK ;
314437}
@@ -345,7 +468,16 @@ int siso_create_connection(sisoconf* conf, const char* ip, const char *port, con
345468 */
346469void siso_close_connection (sisoconf * conf )
347470{
348- if (conf && conf -> sockfd > 0 ) {
471+ if (!conf ) {
472+ return ;
473+ }
474+
475+ if (conf -> ssl ) {
476+ SSL_free (conf -> ssl );
477+ conf -> ssl = NULL ;
478+ }
479+
480+ if (conf -> sockfd > 0 ) {
349481 close (conf -> sockfd );
350482 conf -> sockfd = -1 ;
351483 }
@@ -375,6 +507,7 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
375507
376508 // data sent per cycle
377509 ssize_t sent_now = 0 ;
510+ int ret_code = 1 ;
378511
379512 // Size of remaining data
380513 ssize_t todo = length ;
@@ -390,12 +523,25 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
390523 case SC_SCTP :
391524 sent_now = send (conf -> sockfd , ptr , todo , MSG_NOSIGNAL );
392525 break ;
526+ case SC_TLS :
527+ ret_code = SSL_write_ex (conf -> ssl , ptr , todo , (size_t * )& sent_now );
528+ break ;
393529 default :
394530 break ;
395531 }
396532
397533 // Check for errors
398- if (sent_now == -1 ) {
534+ if (ret_code <= 0 ) {
535+ int err_code = SSL_get_error (conf -> ssl , ret_code );
536+ if (err_code != SSL_ERROR_WANT_READ && err_code != SSL_ERROR_WANT_WRITE ) {
537+ // Connection is broken, close...
538+ conf -> last_error = tls_code_error (err_code );
539+ siso_close_connection (conf );
540+ return SISO_ERR ;
541+ }
542+
543+ // Don't use continue here, the write may be partial.
544+ } else if (sent_now == -1 ) {
399545 if (errno != EAGAIN && errno != EWOULDBLOCK ) {
400546 // Connection broken, close...
401547 conf -> last_error = PERROR_LAST ;
0 commit comments