diff --git a/Makefile b/Makefile index dc91f36..301780f 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,8 @@ LIB_WS = libws.a INCLUDE = include CFLAGS += -Wall -Wextra -O2 CFLAGS += -I $(INCLUDE) -std=c99 -pedantic -LDLIBS = $(LIB_WS) -pthread +#LDLIBS = $(LIB_WS) -pthread -lssl -lcrypto +LDLIBS = $(LIB_WS) -pthread -lssl -lcrypto ARFLAGS = cru MCSS_DIR ?= /usr/bin/ MANPAGES = doc/man/man3 diff --git a/certs/localhost.crt b/certs/localhost.crt new file mode 100644 index 0000000..020a18c --- /dev/null +++ b/certs/localhost.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDDzCCAfegAwIBAgIUOBsdLqSiHICAenBcLw1Gd4GildkwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI0MTExNTA1MDg1OFoXDTI0MTIx +NTA1MDg1OFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAinMabl7EyoJLQkebJJaLfDv+23T1jQvHrHppHM6tGqhh +Fdx+RH568KJepc/cxiuhQEAUp3uYJqPCZm7LXTvnk4zk8zIASoIaMsyDJ4SdEsMe +h4jdALmaXJA89i6diiyhb46FDKE4pfj9xP1Zm6I3/o7b/gU30Do5g7ZVI2z6K+50 +tCCv4eovZ7ES18SDj6WpcutviQKaInJfUVy0+SH68hcf0P0x599LeplTFLNJBXIJ +WEd4JvpLQ02ENrjJQa3oQrD00FthiacEjOgEqHMMoruUdT8x8C2PmBn+JpzcDwC/ +99od5lOHPykokCcFo8eTrZarkSJrBuJ12qzJuDA5OQIDAQABo1kwVzAUBgNVHREE +DTALgglsb2NhbGhvc3QwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMB +MB0GA1UdDgQWBBRDZOrwgxsoDn8gmf1AZm7bEv0RDjANBgkqhkiG9w0BAQsFAAOC +AQEANMBeZjgqgFoZnAXoKJYSQgvjhATMUIOw/NnvqTlKKKBp5R7R2kOmb/XDgF7Z +0uQc7LRRy9diTVo3uUjlgFb2AfZMozqAqk0oIg2R6KdpMOWmce6j6BQEgtGEsHUx +WmvaSgrOimEMO8MG2Hf+sHWLifhFVsd5eZHEBVRkk6a8ydVTBtntNke2Mt1/99WC +qenTmGHe+WZ0YJouxeqyIpiTauKF1/YV2urQfSI3Dcu/fdUeoitiykp+7XEl8FZY +MUOQxgEMnHYOGhpSbyD6YeonUGse/OZswZNPTWRPhpRRjOE6S5oJDjyZtgI8vzIY +pm0FL5EJ4kRadxcCUx5WPN+KAA== +-----END CERTIFICATE----- diff --git a/certs/localhost.key b/certs/localhost.key new file mode 100644 index 0000000..75b6720 --- /dev/null +++ b/certs/localhost.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCKcxpuXsTKgktC +R5sklot8O/7bdPWNC8esemkczq0aqGEV3H5Efnrwol6lz9zGK6FAQBSne5gmo8Jm +bstdO+eTjOTzMgBKghoyzIMnhJ0Swx6HiN0AuZpckDz2Lp2KLKFvjoUMoTil+P3E +/Vmbojf+jtv+BTfQOjmDtlUjbPor7nS0IK/h6i9nsRLXxIOPpaly62+JApoicl9R +XLT5IfryFx/Q/THn30t6mVMUs0kFcglYR3gm+ktDTYQ2uMlBrehCsPTQW2GJpwSM +6ASocwyiu5R1PzHwLY+YGf4mnNwPAL/32h3mU4c/KSiQJwWjx5OtlquRImsG4nXa +rMm4MDk5AgMBAAECggEAAT3LN8BgiBbPPTDGbLdvWH2yuS3FKl63y318/gnB3lZy +aWdk1/Eg4dG44sh4eKvpOhng/YfziywmePPnHabE+qsVnfMdss1UFg7Eocjz/hgz +CMDSx3gCkPAnpcbw+W1RRL5sxbpX11AiWV7yq90yXpVSFmbeU18ER0CuJn9KhnSN +ZaK3CuyS1vwdUbk/vtE1cV/RkbNlHTLAwvSUrxUz/pKCjxSTluJf6OrvzJYmaYeV +g2Fcw5YBZwmXWic0TtZRc0jwuv1PgjqoLWkt/KcvGOJi9XlLl1BmSWWxTc9WRNN3 +8SbOZfbJTODb8RgZvwbbEZJfpjy2Ls+SGe18KiK98QKBgQC/78V9eliDJgKOkZvP +dgB+DdHBQFZmWw5L5SgoD6lYuHAKalJtdxoMHJvzsVerTkBTDH4MOaZALacGbSEi +TxVn7FHwBz3I5c8IUHeLn6Z+Sdvs0oFGCtVflR104sBIX5xR7uKl5Mp7p0lJ5C3T +ojEBg1ZvTRArgZBLiRe5jKjcsQKBgQC4qRRFdUu+oy3CCzUUIfFLmNQ4DOUisYC8 +zO8qp9iNaWi6a1iEvlUMRLv8GpJYbFFxCvu7IRuho1lR89N2WrdOrr9KhT604rkm +MUiZx/nPLNNOmw/LuApvKStj2jWGRbKfGjrrLDQITv8m2NO+jrHrxw5rmERiCVJr +4o/1YRmnCQKBgF7B3itDkp00z8BZDKZQqI+S/QI+ZClmPNmlSbz1dnKxo8bQlN8s +FLT0Dt4xqImPOVyG51fbwkMfBr26dMg+aOAEoLMysAMVya9eIpbQ+96PUj9J8b6F +rK/iq0dAhxNz+HXOzSB/oqOHhDwxpZo9EZqgn7SVRC1marmP9iGJ2vgRAoGAbAU0 +P4FjAqyv+r/Vl+ZXIZLQKc2ZSFcvaMI08e3npqlPwyVCTbyNQbT/hnUAwMr0RT1Y +jlnKsR4+BMuuGqDsdBGTAwO479Vk22ue/Z5WipO9NFFOxyvspqQdprah8t7Yo2uT +pbhg469aYJ4cF2+fsr3puJUFA5hSZ5vL/I2FlQkCgYANv8fNaei0EYfuP92lb+re +rpFjfpjP6ZNJjeWRoUHV7hP2IuSSscE7VFjGtzcebyhqAsJnEltAqaKU/iG+Z6tw +si+GI73cief/jVU7jPNmwSkSL2djptRG87W6mSRc0u+Bbni08mx2ocdtJYwkcDcr +Oe3L9UbIguutuWZ4d0rnqg== +-----END PRIVATE KEY----- diff --git a/examples/echo/echo.c b/examples/echo/echo.c index 058c3cc..f176803 100644 --- a/examples/echo/echo.c +++ b/examples/echo/echo.c @@ -125,6 +125,8 @@ int main(void) */ .host = "0.0.0.0", .port = 8080, + //.cert = "certs/localhost.crt", + //.cert_key = "certs/localhost.key", .thread_loop = 0, .timeout_ms = 1000, .evs.onopen = &onopen, diff --git a/examples/echo/echo.html b/examples/echo/echo.html index dcac1cc..7db9196 100644 --- a/examples/echo/echo.html +++ b/examples/echo/echo.html @@ -121,9 +121,9 @@

wsServer

onclick="document.getElementById('txtServer').value='ws://localhost:8080'"> - - + +

diff --git a/include/ws.h b/include/ws.h index 900d03b..6d50312 100644 --- a/include/ws.h +++ b/include/ws.h @@ -280,6 +280,16 @@ extern "C" { * @brief Listening port */ uint16_t port; +#ifdef ENABLE_SSL + /** + * @brief SSL certificate path (if any) (MUST to enable "wss") + */ + const char *cert; + /** + * @brief SSL private key path (if any) (MUST to enable "wss") + */ + const char *cert_key; +#endif /** * @brief Whether if the ws_socket() should create a new thread * and be non-blocking (1) or not (0). @@ -305,6 +315,10 @@ extern "C" { /* Internal usage. */ extern int get_handshake_accept(char *wsKey, unsigned char **dest); extern int get_handshake_response(char *hsrequest, char **hsresponse); +#ifdef ENABLE_SSL + extern int ssl_init(const char *cert, const char *cert_key); + extern void ssl_end(void); +#endif /* External usage. */ extern char *ws_getaddress(ws_cli_conn_t client); diff --git a/src/ws.c b/src/ws.c index 7ee76a5..355051e 100644 --- a/src/ws.c +++ b/src/ws.c @@ -14,6 +14,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see */ +//#define ENABLE_SSL #define _POSIX_C_SOURCE 200809L #include #include @@ -27,6 +28,11 @@ #include #include +#ifdef ENABLE_SSL +#include +#include +#endif + /* clang-format off */ #ifndef _WIN32 #include @@ -59,6 +65,13 @@ typedef int socklen_t; * @brief wsServer main routines. */ +#ifdef ENABLE_SSL +/** + * @brief SSL context + */ + SSL_CTX *ssl_ctx; +#endif + /** * @brief Client socks. */ @@ -88,6 +101,11 @@ struct ws_connection int32_t current_ping_id; pthread_mutex_t mtx_ping; +#ifdef ENABLE_SSL + /* SSL */ + SSL *ssl; +#endif + /* Connection context */ void *connection_context; @@ -341,7 +359,11 @@ static ssize_t send_all( pthread_mutex_lock(&client->mtx_snd); while (len) { +#ifdef ENABLE_SSL + r = client->ssl ? SSL_write(client->ssl, p, len) : send(client->client_sock, p, len, flags); +#else r = send(client->client_sock, p, len, flags); +#endif if (r == -1) { pthread_mutex_unlock(&client->mtx_snd); @@ -373,6 +395,15 @@ static void close_client(struct ws_connection *client, int lock) set_client_state(client, WS_STATE_CLOSED); +#ifdef ENABLE_SSL + /* Close the client SSL instance */ + if (client->ssl) + { + SSL_shutdown(client->ssl); + SSL_free(client->ssl); + } +#endif + close_socket(client->client_sock); /* Destroy client mutexes and clear fd 'slot'. */ @@ -656,7 +687,11 @@ static int ws_sendframe_internal(struct ws_connection *client, const char *msg, output = 0; if (client && port == 0) { +#ifdef ENABLE_SSL + output = client->ssl ? SSL_write(client->ssl, response, idx_response) : SEND(client, response, idx_response); +#else output = SEND(client, response, idx_response); +#endif goto skip_broadcast; } @@ -671,7 +706,11 @@ static int ws_sendframe_internal(struct ws_connection *client, const char *msg, get_client_state(cli) == WS_STATE_OPEN && (cli->ws_srv.port == port)) { +#ifdef ENABLE_SSL + if ((send_ret = cli->ssl ? SSL_write(cli->ssl, response, idx_response) : SEND(cli, response, idx_response)) != -1) +#else if ((send_ret = SEND(cli, response, idx_response)) != -1) +#endif output += send_ret; else { @@ -1027,7 +1066,11 @@ static int do_handshake(struct ws_frame_data *wfd) ssize_t n; /* Read/Write bytes. */ /* Read the very first client message. */ +#ifdef ENABLE_SSL + if ((n = (wfd->client)->ssl ? SSL_read((wfd->client)->ssl, wfd->frm, sizeof(wfd->frm) - 1) : RECV(wfd->client, wfd->frm, sizeof(wfd->frm) - 1)) < 0) +#else if ((n = RECV(wfd->client, wfd->frm, sizeof(wfd->frm) - 1)) < 0) +#endif return (-1); /* Advance our pointers before the first next_byte(). */ @@ -1055,7 +1098,11 @@ static int do_handshake(struct ws_frame_data *wfd) response); /* Send handshake. */ - if (SEND(wfd->client, response, strlen(response)) < 0) +#ifdef ENABLE_SSL + if (((wfd->client)->ssl ? SSL_write((wfd->client)->ssl, response, strlen(response)) : SEND(wfd->client, response, strlen(response))) < 0) +#else + if ((SEND(wfd->client, response, strlen(response))) < 0) +#endif { free(response); DEBUG("As error has occurred while handshaking!\n"); @@ -1182,7 +1229,11 @@ static inline int next_byte(struct ws_frame_data *wfd) /* If empty or full. */ if (wfd->cur_pos == 0 || wfd->cur_pos == wfd->amt_read) { +#ifdef ENABLE_SSL + if ((n = (wfd->client)->ssl ? SSL_read((wfd->client)->ssl, wfd->frm, sizeof(wfd->frm)) : RECV(wfd->client, wfd->frm, sizeof(wfd->frm))) <= 0) +#else if ((n = RECV(wfd->client, wfd->frm, sizeof(wfd->frm))) <= 0) +#endif { wfd->error = 1; DEBUG("An error has occurred while trying to read next byte\n"); @@ -1884,6 +1935,22 @@ static void *ws_accept(void *data) client_socks[i].client_id = get_next_cid(); set_client_address(&client_socks[i]); +#ifdef ENABLE_SSL + /* Accept the SSL instance */ + client_socks[i].ssl = 0; + if (ssl_ctx) + { + SSL *cli_ssl = SSL_new(ssl_ctx); + if (SSL_set_fd(cli_ssl, new_sock)) + { + if (SSL_accept(cli_ssl)) + { + client_socks[i].ssl = cli_ssl; + } + } + } +#endif + if (pthread_mutex_init(&client_socks[i].mtx_state, NULL)) panic("Error on allocating close mutex"); if (pthread_cond_init(&client_socks[i].cnd_state_close, NULL)) @@ -1910,6 +1977,13 @@ static void *ws_accept(void *data) close_socket(new_sock); } +#ifdef ENABLE_SSL + if (ssl_ctx) + { + SSL_CTX_free(ssl_ctx); + } +#endif + free(data); return (data); } @@ -2022,6 +2096,14 @@ int ws_socket(struct ws_server *ws_srv) setvbuf(stdout, NULL, _IONBF, 0); #endif +#ifdef ENABLE_SSL + /* Initialize SSL context */ + if (ssl_init(ws_srv->cert, ws_srv->cert_key)) + { + panic("Unable to initialize SSL context!"); + } +#endif + /* Create socket and bind. */ sock = do_bind_socket(ws_srv); @@ -2099,3 +2181,65 @@ int ws_file(struct ws_events *evs, const char *file) return (0); } #endif + +#ifdef ENABLE_SSL +/** + * @brief Initialization of the SSL functions + * + * @param cert path to the certificate (.pem). + * + * @param cert_key path to the certificate key (.pem). + * + * @return nothing + * + */ +int ssl_init(const char *cert, const char *cert_key) +{ + ssl_ctx = 0; + + if (!cert || !cert_key) + { + printf("SSL DISABLED\n"); + return 0; + } + + if (!*cert || !*cert_key) + { + return -1; + } + + const SSL_METHOD *method; + + method = TLS_server_method(); + + ssl_ctx = SSL_CTX_new(method); + if (!ssl_ctx) + { + return -1; + } + + /* Configure the context of SSL session */ + if (SSL_CTX_use_certificate_file(ssl_ctx, cert, SSL_FILETYPE_PEM) <= 0 || + SSL_CTX_use_PrivateKey_file(ssl_ctx, cert_key, SSL_FILETYPE_PEM) <= 0) + { + return -1; + } + + atexit(ssl_end); + + printf("SSL ENABLED\n"); + + return 0; +} + +/** + * @brief Finalize the SSL session + * + * @return nothing + * + */ +void ssl_end(void) +{ + SSL_CTX_free(ssl_ctx); +} +#endif \ No newline at end of file