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_ssl_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,129 @@ 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+ if (code == SSL_ERROR_SYSCALL ) {
316+ return PERROR_LAST ;
317+ }
318+ ERR_error_string_n (code , sisco_err_msg_buf , sizeof (sisco_err_msg_buf ));
319+ return & sisco_err_msg_buf [0 ];
320+ }
321+
322+ /**
323+ * @brief Get OpenSSL error message for the given code from SSL connection.
324+ */
325+ static inline const char * tls_ssl_code_error (unsigned long code )
326+ {
327+ if (code == SSL_ERROR_SYSCALL ) {
328+ code = ERR_get_error ();
329+ }
330+ return tls_code_error (code );
331+ }
332+
333+ /**
334+ * @brief Get SSL_CTX. Initialize it if it is not initialized.
335+ * @return NULL on failure.
336+ */
337+ SSL_CTX * siso_get_ssl_ctx (sisoconf * conf )
338+ {
339+ if (conf -> ssl_ctx ) {
340+ return conf -> ssl_ctx ;
341+ }
342+
343+ SSL_load_error_strings ();
344+
345+ SSL_CTX * ctx = SSL_CTX_new (TLS_client_method ());
346+ if (!ctx ) {
347+ conf -> last_error = TLS_LAST_ERROR ;
348+ return NULL ;
349+ }
350+
351+ SSL_CTX_set_verify (ctx , SSL_VERIFY_PEER , NULL );
352+
353+ if (!SSL_CTX_set_default_verify_paths (ctx )) {
354+ conf -> last_error = TLS_LAST_ERROR ;
355+ SSL_CTX_free (ctx );
356+ return NULL ;
357+ }
358+
359+ if (!SSL_CTX_set_min_proto_version (ctx , TLS1_2_VERSION )) {
360+ conf -> last_error = TLS_LAST_ERROR ;
361+ SSL_CTX_free (ctx );
362+ return NULL ;
363+ }
364+
365+ SSL_CTX_set_mode (ctx , SSL_MODE_ENABLE_PARTIAL_WRITE );
366+
367+ // There is no way to pass MSG_SIGIGN when writing using OpenSSL without
368+ // creating custom BIO. So we can just ignore SIGPIPE which can occur if
369+ // peer closes the file descriptor.
370+ signal (SIGPIPE , SIG_IGN );
371+
372+ return conf -> ssl_ctx = ctx ;
373+ }
374+
375+ int siso_load_cert (sisoconf * conf , const char * cert_file ) {
376+ SSL_CTX * ctx = siso_get_ssl_ctx (conf );
377+
378+ if (SSL_CTX_use_certificate_chain_file (ctx , cert_file ) <= 0 ) {
379+ conf -> last_error = TLS_LAST_ERROR ;
380+ return SISO_ERR ;
381+ }
382+
383+ if (SSL_CTX_use_PrivateKey_file (ctx , cert_file , SSL_FILETYPE_PEM ) <= 0 ) {
384+ conf -> last_error = TLS_LAST_ERROR ;
385+ return SISO_ERR ;
386+ }
387+
388+ return SISO_OK ;
389+ }
390+
391+ int siso_tls_connect (sisoconf * conf , int new_fd )
392+ {
393+ assert (!conf -> ssl );
394+
395+ SSL_CTX * ctx = siso_get_ssl_ctx (conf );
396+ if (!ctx ) {
397+ return SISO_ERR ;
398+ }
399+
400+ SSL * ssl = SSL_new (ctx );
401+ if (!ssl ) {
402+ conf -> last_error = TLS_LAST_ERROR ;
403+ return SISO_ERR ;
404+ }
405+
406+ BIO * bio = BIO_new (BIO_s_socket ());
407+ if (!bio ) {
408+ conf -> last_error = TLS_LAST_ERROR ;
409+ SSL_free (ssl );
410+ return SISO_ERR ;
411+ }
412+
413+ BIO_set_fd (bio , new_fd , BIO_NOCLOSE );
414+ SSL_set_bio (ssl , bio , bio );
415+
416+ int res = SSL_connect (ssl );
417+ if (res <= 0 ) {
418+ // Get the best error message.
419+ long vres = SSL_get_verify_result (ssl );
420+ if (vres == X509_V_OK ) {
421+ conf -> last_error = TLS_LAST_SSL_ERROR (ssl , res );
422+ } else {
423+ conf -> last_error = X509_verify_cert_error_string (vres );
424+ }
425+ SSL_free (ssl );
426+ return SISO_ERR ;
427+ }
428+
429+ conf -> ssl = ssl ;
430+ return SISO_OK ;
431+ }
432+
284433/**
285434 * \brief Create new socket
286435 */
@@ -309,6 +458,11 @@ int siso_create_socket(sisoconf *conf)
309458 return SISO_ERR ;
310459 }
311460
461+ if (conf -> type == SC_TLS && siso_tls_connect (conf , new_fd ) != SISO_OK ) {
462+ close (new_fd );
463+ return SISO_ERR ;
464+ }
465+
312466 conf -> sockfd = new_fd ;
313467 return SISO_OK ;
314468}
@@ -345,7 +499,16 @@ int siso_create_connection(sisoconf* conf, const char* ip, const char *port, con
345499 */
346500void siso_close_connection (sisoconf * conf )
347501{
348- if (conf && conf -> sockfd > 0 ) {
502+ if (!conf ) {
503+ return ;
504+ }
505+
506+ if (conf -> ssl ) {
507+ SSL_free (conf -> ssl );
508+ conf -> ssl = NULL ;
509+ }
510+
511+ if (conf -> sockfd > 0 ) {
349512 close (conf -> sockfd );
350513 conf -> sockfd = -1 ;
351514 }
@@ -375,6 +538,7 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
375538
376539 // data sent per cycle
377540 ssize_t sent_now = 0 ;
541+ int ret_code = 1 ;
378542
379543 // Size of remaining data
380544 ssize_t todo = length ;
@@ -390,12 +554,25 @@ int siso_send(sisoconf *conf, const char *data, ssize_t length)
390554 case SC_SCTP :
391555 sent_now = send (conf -> sockfd , ptr , todo , MSG_NOSIGNAL );
392556 break ;
557+ case SC_TLS :
558+ ret_code = SSL_write_ex (conf -> ssl , ptr , todo , (size_t * )& sent_now );
559+ break ;
393560 default :
394561 break ;
395562 }
396563
397564 // Check for errors
398- if (sent_now == -1 ) {
565+ if (ret_code <= 0 ) {
566+ int err_code = SSL_get_error (conf -> ssl , ret_code );
567+ if (err_code != SSL_ERROR_WANT_READ && err_code != SSL_ERROR_WANT_WRITE ) {
568+ // Connection is broken, close...
569+ conf -> last_error = tls_ssl_code_error (err_code );
570+ siso_close_connection (conf );
571+ return SISO_ERR ;
572+ }
573+
574+ // Don't use continue here, the write may be partial.
575+ } else if (sent_now == -1 ) {
399576 if (errno != EAGAIN && errno != EWOULDBLOCK ) {
400577 // Connection broken, close...
401578 conf -> last_error = PERROR_LAST ;
0 commit comments