From fbbea9df3004c0b9e4faa14663a2092521cb749d Mon Sep 17 00:00:00 2001 From: Jianhui Zhao Date: Fri, 10 Nov 2017 18:51:49 +0800 Subject: [PATCH] Basically complete SSL support: Optional OpenSSL and CyaSSl(wolfssl) Signed-off-by: Jianhui Zhao --- CMakeLists.txt | 2 +- example/helloworld.c | 10 ++- src/CMakeLists.txt | 64 +++++++++++---- src/uhttp.c | 23 +++--- src/uhttp.h | 2 + src/uhttp_config.h.in | 6 +- src/uhttp_internal.h | 8 ++ src/uhttp_ssl.c | 186 ++++++++++++++++++++++++++++++++++++++++++ src/uhttp_ssl.h | 12 +++ 9 files changed, 280 insertions(+), 33 deletions(-) mode change 100644 => 100755 src/uhttp_ssl.c create mode 100755 src/uhttp_ssl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ef7ef75..5bdc687 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(libuhttp C) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/Modules/") #set(CMAKE_VERBOSE_MAKEFILE ON) -#add_definitions(--DUH_DEBUG) +#add_definitions(-DUH_DEBUG) add_definitions(-O -Wall -Werror --std=gnu99 -D_GNU_SOURCE) diff --git a/example/helloworld.c b/example/helloworld.c index c474621..2f1ec5b 100755 --- a/example/helloworld.c +++ b/example/helloworld.c @@ -32,7 +32,7 @@ int main(int argc, char **argv) { struct ev_loop *loop = EV_DEFAULT; ev_signal *sig_watcher = NULL; - struct uh_server *srv; + struct uh_server *srv = NULL; uh_log_info("libuhttp version: %s\n", uh_version()); @@ -46,15 +46,19 @@ int main(int argc, char **argv) srv = uh_server_new(loop, "0.0.0.0", 8000); if (!srv) { uh_log_err("uh_server_new failed\n"); - return -1; + goto err; } + if (uh_ssl_init(srv, "server.pem", "server.key") < 0) + goto err; + uh_register_route(srv, "/test", route_test); uh_log_info("Listen on 8000...\n"); ev_run(loop, 0); - + +err: free(sig_watcher); uh_server_free(srv); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e81e15f..61c5f43 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -12,26 +12,59 @@ find_package(CyaSSL) include_directories(${LIBEV_INCLUDE_DIRS} ${HTTPPARSER_INCLUDE_DIRS}) set(EXTRA_LIBS ${LIBEV_LIBRARIES} ${HTTPPARSER_LIBRARIES}) -set(SOURCE_FILES uhttp.c uhttp_log.c uhttp_buf.c) - -set(UHTTP_FOUND_SSL OFF) +set(SOURCE_FILES uhttp.c uhttp_log.c uhttp_buf.c uhttp_ssl.c) option(UHTTP_DISABLE_SSL "Disable ssl support" OFF) +option(UHTTP_USE_OPENSSL "Force use OpenSSL as SSL backend" OFF) +option(UHTTP_USE_CYASSL "Force use CyaSSL as SSL backend" OFF) + +set(UHTTP_SSL_ENABLED OFF) +set(UHTTP_SSL_ENABLED_CONFIG 0) +set(UHTTP_USE_OPENSSL_CONFIG 0) +set(UHTTP_USE_CYASSL_CONFIG 0) if(NOT UHTTP_DISABLE_SSL) - if(OPENSSL_FOUND) - set(UHTTP_FOUND_SSL ON) + if(UHTTP_USE_OPENSSL) + if(NOT OPENSSL_FOUND) + message(FATAL_ERROR "Could NOT find OpenSSL") + endif() + + set(UHTTP_SSL_ENABLED ON) + set(UHTTP_SSL_ENABLED_CONFIG 1) + set(UHTTP_USE_OPENSSL_CONFIG 1) + list(APPEND EXTRA_LIBS ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) - elseif(CYASSL_FOUND) - set(UHTTP_FOUND_SSL ON) + elseif(UHTTP_USE_CYASSL) + if (NOT CYASSL_FOUND) + message(FATAL_ERROR "Could NOT find CyaSSL") + endif() + + set(UHTTP_SSL_ENABLED ON) + set(UHTTP_SSL_ENABLED_CONFIG 1) + set(UHTTP_USE_CYASSL_CONFIG 1) + list(APPEND EXTRA_LIBS ${CYASSL_LIBRARIES}) include_directories(${CYASSL_INCLUDE_DIR}) endif() -endif() -if(UHTTP_FOUND_SSL) - list(APPEND SOURCE_FILES uhttp_ssl.c) + if(NOT UHTTP_SSL_ENABLED) + if(OPENSSL_FOUND) + set(UHTTP_SSL_ENABLED ON) + set(UHTTP_SSL_ENABLED_CONFIG 1) + set(UHTTP_USE_OPENSSL_CONFIG 1) + + list(APPEND EXTRA_LIBS ${OPENSSL_LIBRARIES}) + include_directories(${OPENSSL_INCLUDE_DIR}) + elseif(CYASSL_FOUND) + set(UHTTP_SSL_ENABLED ON) + set(UHTTP_SSL_ENABLED_CONFIG 1) + set(UHTTP_USE_CYASSL_CONFIG 1) + + list(APPEND EXTRA_LIBS ${CYASSL_LIBRARIES}) + include_directories(${CYASSL_INCLUDE_DIR}) + endif() + endif() endif() add_library(uhttp SHARED ${SOURCE_FILES}) @@ -52,7 +85,10 @@ install( ) message("") -message(STATUS "UHTTP_VERSION = ${UHTTP_VERSION_MAJOR}.${UHTTP_VERSION_MINOR}") -message(STATUS "UHTTP_DISABLE_SSL = ${UHTTP_DISABLE_SSL}") -message(STATUS "UHTTP_FOUND_SSL = ${UHTTP_FOUND_SSL}") -message("") \ No newline at end of file +message(STATUS "UHTTP_VERSION: ${UHTTP_VERSION_MAJOR}.${UHTTP_VERSION_MINOR}") +if(UHTTP_USE_OPENSSL_CONFIG) +message(STATUS "UHTTP_SSL_ENABLED: OpenSSL") +elseif(UHTTP_USE_CYASSL_CONFIG) +message(STATUS "UHTTP_SSL_ENABLED: CyaSSL") +endif() +message("") diff --git a/src/uhttp.c b/src/uhttp.c index abd8f50..9a63542 100755 --- a/src/uhttp.c +++ b/src/uhttp.c @@ -1,14 +1,13 @@ #include +#include #include #include -#include #include -#include #include -#include #include "uhttp.h" #include "uhttp_internal.h" +#include "uhttp_ssl.h" static struct { int code; @@ -61,6 +60,8 @@ static void uh_connection_destroy(struct uh_connection *con) ev_timer_stop(loop, &con->timer_watcher); list_del(&con->list); + + uh_ssl_free(con); free(con); } } @@ -201,9 +202,9 @@ static void connection_read_cb(struct ev_loop *loop, ev_io *w, int revents) base = buf->base + buf->len; - len = read(w->fd, base, UH_BUFFER_SIZE); + len = uh_ssl_read(con, base, UH_BUFFER_SIZE); if (unlikely(len < 0)) { - if (errno == EINTR) + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) return; uh_log_err("read"); uh_send_error(con, 500, NULL); @@ -234,7 +235,7 @@ static void connection_write_cb(struct ev_loop *loop, ev_io *w, int revents) struct uh_buf *buf = &con->write_buf; if (buf->len > 0) { - int len = write(w->fd, buf->base, buf->len); + int len = uh_ssl_write(con, buf->base, buf->len); uh_buf_remove(buf, len); } @@ -252,8 +253,6 @@ static void connection_write_cb(struct ev_loop *loop, ev_io *w, int revents) static void uh_accept_cb(struct ev_loop *loop, ev_io *w, int revents) { int sock = -1; - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); struct uh_server *srv = container_of(w, struct uh_server, read_watcher); struct uh_connection *con = NULL; ev_io *read_watcher, *write_watcher; @@ -268,15 +267,13 @@ static void uh_accept_cb(struct ev_loop *loop, ev_io *w, int revents) con->srv = srv; list_add(&con->list, &srv->connections); - sock = accept4(w->fd, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); + sock = uh_ssl_accept(srv, con); if (unlikely(sock < 0)) { - if (errno != EINTR) + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) uh_log_err("accept"); goto err; } - con->sock = sock; - read_watcher = &con->read_watcher; ev_io_init(read_watcher, connection_read_cb, sock, EV_READ); ev_io_start(loop,read_watcher); @@ -372,6 +369,8 @@ void uh_server_free(struct uh_server *srv) free(r->path); free(r); } + + uh_ssl_ctx_free(srv); free(srv); } diff --git a/src/uhttp.h b/src/uhttp.h index 3ee8ae8..ea8035b 100755 --- a/src/uhttp.h +++ b/src/uhttp.h @@ -86,4 +86,6 @@ int uh_register_route(struct uh_server *srv, const char *path, uh_route_handler_ struct uh_value *uh_get_url(struct uh_connection *con); struct uh_value *uh_get_header(struct uh_connection *con, const char *name); +int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key); + #endif diff --git a/src/uhttp_config.h.in b/src/uhttp_config.h.in index 5f44e68..1662641 100755 --- a/src/uhttp_config.h.in +++ b/src/uhttp_config.h.in @@ -5,8 +5,8 @@ #define UHTTP_VERSION_MINOR @UHTTP_VERSION_MINOR@ #define UHTTP_VERSION_STRING "@UHTTP_VERSION_MAJOR@.@UHTTP_VERSION_MINOR@" -#define UHTTP_SSL_ENABLED @SSL_ENABLED@ -#define UHTTP_USE_OPENSSL @USE_OPENSSL@ -#define UHTTP_USE_CYASSL @USE_CYASSL@ +#define UHTTP_SSL_ENABLED @UHTTP_SSL_ENABLED_CONFIG@ +#define UHTTP_USE_OPENSSL @UHTTP_USE_OPENSSL_CONFIG@ +#define UHTTP_USE_CYASSL @UHTTP_USE_CYASSL_CONFIG@ #endif diff --git a/src/uhttp_internal.h b/src/uhttp_internal.h index 6ee16af..a5029bf 100755 --- a/src/uhttp_internal.h +++ b/src/uhttp_internal.h @@ -1,6 +1,8 @@ #ifndef _UHTTP_INTERNAL_H #define _UHTTP_INTERNAL_H +#include + #include "list.h" #include "uhttp.h" @@ -21,6 +23,9 @@ struct uh_route { struct uh_server { int sock; +#if (UHTTP_SSL_ENABLED) + void *ssl_ctx; +#endif ev_io read_watcher; struct ev_loop *loop; struct list_head routes; @@ -41,6 +46,9 @@ struct uh_request { struct uh_connection { int sock; +#if (UHTTP_SSL_ENABLED) + void *ssl; +#endif unsigned char flags; struct uh_buf read_buf; struct uh_buf write_buf; diff --git a/src/uhttp_ssl.c b/src/uhttp_ssl.c old mode 100644 new mode 100755 index e69de29..36ab163 --- a/src/uhttp_ssl.c +++ b/src/uhttp_ssl.c @@ -0,0 +1,186 @@ +#include "uhttp_ssl.h" +#include +#include + +#if (UHTTP_USE_OPENSSL) +#include +#include +#elif (UHTTP_USE_CYASSL) +#include +#endif + +int uh_ssl_init(struct uh_server *srv, const char *cert, const char *key) +{ +#if (UHTTP_USE_OPENSSL) + SSL_CTX *ctx = NULL; +#elif (UHTTP_USE_CYASSL) + WOLFSSL_CTX *ctx = NULL; +#endif + +#if (UHTTP_USE_OPENSSL) + SSL_library_init(); + + /* creates a new SSL_CTX object */ + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ctx) { + uh_log_err("Failed to create SSL context"); + return -1; + } + + /* loads the first certificate stored in file into ctx */ + if (!SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM)) { + uh_log_err("OpenSSL Error: loading certificate file failed"); + goto err; + } + + /* + * adds the first private RSA key found in file to ctx. + * + * checks the consistency of a private key with the corresponding + * certificate loaded into ctx. If more than one key/certificate + * pair (RSA/DSA) is installed, the last item installed will be checked. + */ + if (!SSL_CTX_use_RSAPrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) { + uh_log_err("OpenSSL Error: loading key failed"); + goto err; + } + +#elif (UHTTP_USE_CYASSL) + /* Initialize wolfSSL */ + wolfSSL_Init(); + + /* Create the WOLFSSL_CTX */ + ctx = wolfSSL_CTX_new(wolfSSLv23_server_method()); + if (!ctx) { + uh_log_err("Failed to create wolfSSL context"); + return -1; + } + + /* Load server certificates into WOLFSSL_CTX */ + if (wolfSSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) != SSL_SUCCESS) { + uh_log_err("wolfSSL Error: loading certificate file failed"); + goto err; + } + + /* Load keys */ + if (wolfSSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != SSL_SUCCESS){ + uh_log_err("wolfSSL Error: loading key failed"); + goto err; + } + +#endif + +#if (UHTTP_SSL_ENABLED) + srv->ssl_ctx = ctx; +#endif + return 0; +#if (UHTTP_SSL_ENABLED) +err: +#if (UHTTP_USE_OPENSSL) + SSL_CTX_free(ctx); +#elif (UHTTP_USE_CYASSL) + wolfSSL_CTX_free(ctx); + wolfSSL_Cleanup(); +#endif +#endif + + return -1; +} + +void uh_ssl_ctx_free(struct uh_server *srv) +{ +#if (UHTTP_SSL_ENABLED) + if (!srv->ssl_ctx) + return; +#endif + +#if (UHTTP_USE_OPENSSL) + SSL_CTX_free(srv->ssl_ctx); +#elif (UHTTP_USE_CYASSL) + wolfSSL_CTX_free(srv->ssl_ctx); +#endif +} + +void uh_ssl_free(struct uh_connection *con) +{ +#if (UHTTP_SSL_ENABLED) + if (!con->ssl) + return; +#endif + +#if (UHTTP_USE_OPENSSL) + SSL_free(con->ssl); +#elif (UHTTP_USE_CYASSL) + wolfSSL_free(con->ssl); +#endif +} + +int uh_ssl_read(struct uh_connection *con, void *buf, int count) +{ +#if (UHTTP_SSL_ENABLED) + if (!con->ssl) + return read(con->sock, buf, count); +#endif + +#if (UHTTP_USE_OPENSSL) + return SSL_read(con->ssl, buf, count); +#elif (UHTTP_USE_CYASSL) + return wolfSSL_read(con->ssl, buf, count); +#endif + + return read(con->sock, buf, count); +} + +int uh_ssl_write(struct uh_connection *con, void *buf, int count) +{ +#if (UHTTP_SSL_ENABLED) + if (!con->ssl) + return write(con->sock, buf, count); +#endif + +#if (UHTTP_USE_OPENSSL) + return SSL_write(con->ssl, buf, count); +#elif (UHTTP_USE_CYASSL) + return wolfSSL_write(con->ssl, buf, count); +#endif + + return write(con->sock, buf, count); +} + +int uh_ssl_accept(struct uh_server *srv, struct uh_connection *con) +{ + int sock = -1; + + sock = accept4(srv->sock, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC); + if (sock < 0) + return sock; + + con->sock = sock; + +#if (UHTTP_SSL_ENABLED) + if (!srv->ssl_ctx) + return sock; +#endif + +#if (UHTTP_USE_OPENSSL) + con->ssl = SSL_new(srv->ssl_ctx); + if (!con->ssl) + return -1; + + SSL_set_fd(con->ssl, sock); + + if (!SSL_accept(con->ssl)) { + uh_log_err("SSL_accept Error: %s", ERR_reason_error_string(ERR_get_error())); + return -1; + } +#elif (UHTTP_USE_CYASSL) + con->ssl = wolfSSL_new(srv->ssl_ctx); + if (!con->ssl) + return -1; + + wolfSSL_set_fd(con->ssl, sock); +#endif + + return sock; +} + diff --git a/src/uhttp_ssl.h b/src/uhttp_ssl.h new file mode 100755 index 0000000..05203a3 --- /dev/null +++ b/src/uhttp_ssl.h @@ -0,0 +1,12 @@ +#ifndef _UHTTP_SSL_H +#define _UHTTP_SSL_H + +#include "uhttp_internal.h" + +void uh_ssl_ctx_free(struct uh_server *srv); +void uh_ssl_free(struct uh_connection *con); +int uh_ssl_read(struct uh_connection *con, void *buf, int count); +int uh_ssl_write(struct uh_connection *con, void *buf, int count); +int uh_ssl_accept(struct uh_server *srv, struct uh_connection *con); + +#endif