From f1d2bd1d5fb47b28c738de781f85a15d85f6ac32 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 20 Nov 2017 19:03:08 -0500 Subject: [PATCH 01/16] deprecated unset_hook and set_hook / cleanup --- CMakeLists.txt | 36 +++++++++++++++++------------------- evhtp.c | 29 ++++++++++++++++++++--------- include/evhtp/evhtp.h | 24 +++++------------------- parser.c | 6 ------ thread.c | 6 ------ 5 files changed, 42 insertions(+), 59 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ba3a6f..3617d97 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,16 +24,19 @@ if (EVHTP_THR_SHARED_PIPE) add_definitions(-DEVTHR_SHARED_PIPE) endif() -CHECK_include_FILES(strings.h HAVE_STRINGS_H) -CHECK_include_FILES(string.h HAVE_STRING_H) -CHECK_include_FILES(stdlib.h HAVE_STDLIB_H) -CHECK_include_FILES(sys/time.h HAVE_SYS_TIME_H) -CHECK_include_FILES(sys/times.h HAVE_SYS_TIMES_H) -CHECK_include_FILES(unistd.h HAVE_UNISTD_H) -CHECK_include_FILES(stdarg.h HAVE_STDARG_PROTOTYPES) -CHECK_include_FILES(sys/tree.h HAVE_SYS_TREE) -CHECK_include_FILES(sys/queue.h HAVE_SYS_QUEUE) -CHECK_include_FILES(sys/un.h HAVE_SYS_UN) +check_include_files (stdlib.h HAVE_STDLIB_H) +check_include_files (string.h HAVE_STRING_H) +check_include_files (stdint.h HAVE_STDINT_H) +check_include_files (errno.h HAVE_ERRNO_H) +check_include_files (strings.h HAVE_STRINGS_H) +check_include_files (inttypes.h HAVE_INTTYPES_H) +check_include_files (limits.h HAVE_LIMITS_H) + +check_include_files (unistd.h HAVE_UNISTD_H) +check_include_files (stdarg.h HAVE_STDARG_PROTOTYPES) +check_include_files (sys/tree.h HAVE_SYS_TREE) +check_include_files (sys/queue.h HAVE_SYS_QUEUE) +check_include_files (sys/un.h HAVE_SYS_UN) CHECK_TYPE_SIZE("int" SIZEOF_INT) CHECK_TYPE_SIZE("long" SIZEOF_LONG) @@ -189,11 +192,11 @@ if (NOT EVHTP_DISABLE_EVTHR) list (APPEND LIBEVHTP_EXTERNAL_LIBS pthread) endif() -add_library(evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES}) -target_link_libraries(evhtp ${LIBEVHTP_EXTERNAL_LIBS}) +add_library (evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES}) +target_link_libraries (evhtp ${LIBEVHTP_EXTERNAL_LIBS}) if (EVHTP_BUILD_SHARED) - set_target_properties(evhtp PROPERTIES SOVERSION "${PROJECT_VERSION}") + set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" 0 OUTPUT_NAME "evhtp") endif() add_subdirectory(examples) @@ -212,6 +215,7 @@ install ( FILES ${PROJECT_SOURCE_DIR}/include/evhtp/evhtp.h ${PROJECT_SOURCE_DIR}/include/evhtp/parser.h + ${PROJECT_BINARY_DIR}/include/evhtp/config.h DESTINATION ${INCLUDE_INSTALL_DIR}/evhtp) @@ -221,12 +225,6 @@ install ( DESTINATION ${INCLUDE_INSTALL_DIR}) -install ( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/include/evhtp/config.h - DESTINATION - ${INCLUDE_INSTALL_DIR}/evhtp) - if (NOT EVHTP_DISABLE_EVTHR) install ( FILES diff --git a/evhtp.c b/evhtp.c index c818a9f..843050d 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4352,10 +4352,27 @@ htp__set_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void return 0; } /* htp__set_hook_ */ +static int +htp__unset_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type) { + return htp__set_hook_(hooks, type, NULL, NULL); +} + +int +evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type) +{ + return htp__unset_hook_(&callback->hooks, type); +} + int -evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) +evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type) { - return htp__set_hook_(hooks, type, cb, arg); + return htp__unset_hook_(&req->hooks, type); +} + +int +evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type) +{ + return htp__unset_hook_(&conn->hooks, type); } int @@ -4376,12 +4393,6 @@ evhtp_connection_set_hook(evhtp_connection_t * conn, evhtp_hook_type type, evhtp return htp__set_hook_(&conn->hooks, type, cb, arg); } -int -evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type) -{ - return evhtp_set_hook(hooks, type, NULL, NULL); -} - int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) { @@ -4413,7 +4424,7 @@ evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) } for (i = 0; hooklist_[i].type != -1; i++) { - if (evhtp_unset_hook(hooks, hooklist_[i].type) == -1) { + if (htp__unset_hook_(hooks, hooklist_[i].type) == -1) { return -1; } } diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index 0c66428..ae89238 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -162,7 +162,7 @@ typedef evhtp_res (* evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg typedef evhtp_res (* evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg); typedef evhtp_res (* evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg); typedef evhtp_res (* evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg); -typedef evhtp_res (* evhtp_hook_read_cb)(evhtp_request_t * req, evbuf_t * buf, void * arg); +typedef evhtp_res (* evhtp_hook_read_cb)(evhtp_request_t * req, struct evbuffer * buf, void * arg); typedef evhtp_res (* evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg); typedef evhtp_res (* evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg); typedef evhtp_res (* evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg); @@ -395,8 +395,8 @@ struct evhtp_request_s { evhtp_connection_t * conn; /**< the associated connection */ evhtp_hooks_t * hooks; /**< request specific hooks */ evhtp_uri_t * uri; /**< request URI information */ - evbuf_t * buffer_in; /**< buffer containing data from client */ - evbuf_t * buffer_out; /**< buffer containing data to client */ + struct evbuffer * buffer_in; /**< buffer containing data from client */ + struct evbuffer * buffer_out; /**< buffer containing data to client */ evhtp_headers_t * headers_in; /**< headers from client */ evhtp_headers_t * headers_out; /**< headers to client */ evhtp_proto proto; /**< HTTP protocol used */ @@ -765,24 +765,10 @@ EVHTP_EXPORT evhtp_callback_t * evhtp_get_cb(evhtp_t * htp, const char * needle) * * @return 0 on success, -1 on error (if hooks is NULL, it is allocated) */ -EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg) -DEPRECATED("use evhtp_[connection|request|callback]_set_hook() instead of set_hook directly"); - EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_request_set_hook(evhtp_request_t * r, evhtp_hook_type type, evhtp_hook cb, void * arg); EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type type, evhtp_hook hookcb, void * arg); -/** - * @brief remove a specific hook from being called. - * - * @param hooks - * @param type - * - * @return - */ -EVHTP_EXPORT int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type); - - /** * @brief removes all hooks. * @@ -887,7 +873,7 @@ EVHTP_EXPORT void evhtp_send_reply(evhtp_request_t * request, evhtp_res code); * but for the weak of heart. */ EVHTP_EXPORT void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code); -EVHTP_EXPORT void evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf); +EVHTP_EXPORT void evhtp_send_reply_body(evhtp_request_t * request, struct evbuffer * buf); EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request); /** @@ -914,7 +900,7 @@ EVHTP_EXPORT void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_ * @param request * @param buf */ -EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf); +EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf); /** * @brief call when all chunks have been sent and you wish to send the last diff --git a/parser.c b/parser.c index f8dfeb3..cda246f 100644 --- a/parser.c +++ b/parser.c @@ -1,12 +1,6 @@ -#include -#include #include #include -#include -#include -#include #include -#include #include "internal.h" #include "evhtp/parser.h" diff --git a/thread.c b/thread.c index 8f03832..7659cee 100644 --- a/thread.c +++ b/thread.c @@ -1,15 +1,9 @@ #define _GNU_SOURCE #include #include -#include #include -#include #include -#include -#include #ifndef WIN32 -#include -#include #include #endif From 45003e13a42ed5c6f1a29c4cf3925015b9756bbd Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 20 Nov 2017 19:38:02 -0500 Subject: [PATCH 02/16] updated travis configuration --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 840b997..56b6873 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,11 +4,11 @@ compiler: - clang sudo: required -iinstall: +before_install: - sudo apt-get update -qq - - sudo apt-get install libevent-dev + - sudo apt-get install libevent-dev libonig-dev script: - - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build . + - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make examples notifications: - irc "irc.oftc.net#libevhtp" From c7978180ce7c0728ce77af82e0d879b2e2735348 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 20 Nov 2017 19:42:55 -0500 Subject: [PATCH 03/16] updated travis build icon --- README.markdown | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.markdown b/README.markdown index fff71d4..fa42132 100644 --- a/README.markdown +++ b/README.markdown @@ -1,7 +1,7 @@ | ![LOGO](http://i.imgur.com/uBd4iIz.png) |

Libevhtp

| | :------------- | -------------: | -[![Build Status](https://travis-ci.org/ellzey/libevhtp.svg?branch=develop)](https://travis-ci.org/ellzey/libevhtp) +[![Build Status](https://travis-ci.org/criticalstack/libevhtp.svg?branch=develop)](https://travis-ci.org/ellzey/libevhtp) Coverity Scan Build Status @@ -51,7 +51,7 @@ Libevhtp attempts to address these problems along with a wide variety of cool me 1. Create a parent evhtp_t structure. 2. Assign callbacks to the parent for specific URIs or posix-regex based URI's 3. Optionally assign per-connection hooks (see hooks) to the callbacks. -4. Optionally assign pre-accept and post-accept callbacks for incoming connections. +4. Optionally assign pre-accept and post-accept callbacks for incoming connections. 5. Optionally enable built-in threadpool for connection handling (lock-free, and non-blocking). 6. Optionally morph your server to HTTPS. 7. Start the evhtp listener. @@ -78,7 +78,7 @@ Libevhtp attempts to address these problems along with a wide variety of cool me main(int argc, char ** argv) { evbase_t * evbase = event_base_new(); evhtp_t * htp = evhtp_new(evbase, NULL); - + evhtp_set_cb(htp, "/test", testcb, NULL); evhtp_bind_socket(htp, "0.0.0.0", 8080, 1024); event_base_loop(evbase, 0); @@ -89,12 +89,12 @@ Libevhtp attempts to address these problems along with a wide variety of cool me ## Is evhtp thread-safe? For simple usage with evhtp_use_threads(), yes. But for more extreme cases: -sorta, you are bound to the thread mechanisms of libevent itself. +sorta, you are bound to the thread mechanisms of libevent itself. But with proper design around libevhtp, thread issues can be out-of-sight, -out-of-mind. +out-of-mind. -What do you mean by this "proper design" statement? +What do you mean by this "proper design" statement? Refer to the code in ./examples/thread_design.c. The comments go into great detail of the hows and whys for proper design using libevhtp's threading model. @@ -113,7 +113,7 @@ with redis. ## Performance stuff -While we never documented any benchmark publically, +While we never documented any benchmark publically, the popular open source project [ZIMG](http://zimg.buaa.us) did a bit of that for us.The ZIMG team decided to move away from NGINX to libevhtp for their software, and the results were pretty outstanding. Here is a graph showing their From 77355162e2ca0b64b9f645b0b789e50848f55b2e Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 20 Nov 2017 19:43:56 -0500 Subject: [PATCH 04/16] updated travis build icon --- README.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.markdown b/README.markdown index fa42132..3c6f327 100644 --- a/README.markdown +++ b/README.markdown @@ -1,7 +1,7 @@ | ![LOGO](http://i.imgur.com/uBd4iIz.png) |

Libevhtp

| | :------------- | -------------: | -[![Build Status](https://travis-ci.org/criticalstack/libevhtp.svg?branch=develop)](https://travis-ci.org/ellzey/libevhtp) +[![Build Status](https://travis-ci.org/criticalstack/libevhtp.svg?branch=develop)](https://travis-ci.org/criticalstack/libevhtp)
Coverity Scan Build Status From cce97eac6f3b4ffa41ef49534e8e73f65b4d1c74 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Tue, 21 Nov 2017 10:38:33 -0500 Subject: [PATCH 05/16] Update README.markdown --- README.markdown | 1 + 1 file changed, 1 insertion(+) diff --git a/README.markdown b/README.markdown index 3c6f327..c965a8a 100644 --- a/README.markdown +++ b/README.markdown @@ -17,6 +17,7 @@ probably not very awesome, it's best to look at test.c to see advanced usage. ## Optional Dependencies * [OpenSSL](http://openssl.org) * pthreads +* [onig (regex)](https://github.com/kkos/oniguruma) ## Building * cd build From 495558aca5009451519511e1e57974b0e0998993 Mon Sep 17 00:00:00 2001 From: Tony Lambiris Date: Tue, 21 Nov 2017 13:48:15 -0500 Subject: [PATCH 06/16] Latest revision to support multiple SSL versions --- evhtp.c | 74 ++++++++++++++----------------------------- include/evhtp/evhtp.h | 12 +++++-- 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/evhtp.c b/evhtp.c index 43536e6..aed68ac 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2565,10 +2565,6 @@ htp__connection_new_(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); -#ifdef EVHTP_FUTURE_USE - TAILQ_INIT(&connection->pending); -#endif - return connection; } /* htp__connection_new_ */ @@ -2715,16 +2711,14 @@ htp__ssl_thread_lock_(int mode, int type, const char * file, int line) static void htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess) { - evhtp_t * htp; - evhtp_ssl_cfg_t * cfg; - unsigned char * sid; - unsigned int slen; - - htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); - cfg = htp->ssl_cfg; + evhtp_t * htp; + evhtp_ssl_cfg_t * cfg; + evhtp_ssl_data_t * sid; + unsigned int slen; - sid = sess->session_id; - slen = sess->session_id_length; + htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); + cfg = htp->ssl_cfg; + sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); if (cfg->scache_del) { @@ -2737,7 +2731,7 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; - unsigned char * sid; + evhtp_ssl_data_t * sid; int slen; connection = (evhtp_connection_t *)SSL_get_app_data(ssl); @@ -2745,10 +2739,9 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { return 0; /* We cannot get the ssl_cfg */ } - cfg = connection->htp->ssl_cfg; - sid = sess->session_id; - slen = sess->session_id_length; + cfg = connection->htp->ssl_cfg; + sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); SSL_set_timeout(sess, cfg->scache_timeout); @@ -2761,7 +2754,7 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) } static evhtp_ssl_sess_t * -htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, unsigned char * sid, int sid_len, int * copy) +htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; @@ -2815,18 +2808,20 @@ htp__ssl_servername_(evhtp_ssl_t * ssl, int * unused, void * arg) if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname))) { + SSL_CTX * ctx = SSL_get_SSL_CTX(ssl); + connection->htp = evhtp_vhost; HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI); SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx); - SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx)); + SSL_set_options(ssl, SSL_CTX_get_options(ctx)); if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || (SSL_num_renegotiations(ssl) == 0)) { - SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx), - SSL_CTX_get_verify_callback(ssl->ctx)); + SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), + SSL_CTX_get_verify_callback(ctx)); } return SSL_TLSEXT_ERR_OK; @@ -4619,13 +4614,8 @@ evhtp_ssl_use_threads(void) int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) { -#ifdef EVHTP_ENABLE_FUTURE_STUFF - evhtp_ssl_scache_init init_cb = NULL; - evhtp_ssl_scache_add add_cb = NULL; - evhtp_ssl_scache_get get_cb = NULL; - evhtp_ssl_scache_del del_cb = NULL; -#endif - long cache_mode; + long cache_mode; + unsigned char c; if (cfg == NULL || htp == NULL || cfg->pemfile == NULL) { @@ -4708,7 +4698,12 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) if (cfg->x509_chk_issued_cb != NULL) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb; +#else + X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb); +#endif + /*SSL_CTX_set_cert_store(htp->ssl_ctx, cfg->x509_chk_issued_cb); */ } if (cfg->verify_depth) @@ -4720,29 +4715,6 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) case evhtp_ssl_scache_type_disabled: cache_mode = SSL_SESS_CACHE_OFF; break; -#ifdef EVHTP_ENABLE_FUTURE_STUFF - case evhtp_ssl_scache_type_user: - cache_mode = SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL | - SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; - - init_cb = cfg->scache_init; - add_cb = cfg->scache_add; - get_cb = cfg->scache_get; - del_cb = cfg->scache_del; - break; - case evhtp_ssl_scache_type_builtin: - cache_mode = SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL | - SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; - - init_cb = htp__ssl_builtin_init_; - add_cb = htp__ssl_builtin_add_; - get_cb = htp__ssl_builtin_get_; - del_cb = htp__ssl_builtin_del_; - break; -#endif - case evhtp_ssl_scache_type_internal: default: cache_mode = SSL_SESS_CACHE_SERVER; break; diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index 3eaa6c8..133311c 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -44,6 +44,11 @@ typedef SSL evhtp_ssl_t; typedef SSL_CTX evhtp_ssl_ctx_t; typedef X509 evhtp_x509_t; typedef X509_STORE_CTX evhtp_x509_store_ctx_t; +#if OPENSSL_VERSION_NUMBER < 0x10100000L +typedef unsigned char evhtp_ssl_data_t; +#else +typedef const unsigned char evhtp_ssl_data_t; +#endif #else typedef void evhtp_ssl_sess_t; typedef void evhtp_ssl_t; @@ -180,9 +185,10 @@ typedef int (* evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); typedef EVP_PKEY * (* evhtp_ssl_decrypt_cb)(char * privfile); -typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess); -typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len); -typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len); +typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess); +typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, evhtp_ssl_data_t * sid, int sid_len); +typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len); + typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif From 4e353ba53dbb88335437aac37e3270e0857f2c04 Mon Sep 17 00:00:00 2001 From: Tony Lambiris Date: Tue, 21 Nov 2017 13:48:15 -0500 Subject: [PATCH 07/16] Latest revision to support multiple SSL versions --- evhtp.c | 74 ++++++++++++++----------------------------- include/evhtp/evhtp.h | 12 +++++-- 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/evhtp.c b/evhtp.c index 843050d..fbce474 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2754,10 +2754,6 @@ htp__connection_new_(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); -#ifdef EVHTP_FUTURE_USE - TAILQ_INIT(&connection->pending); -#endif - return connection; } /* htp__connection_new_ */ @@ -2904,16 +2900,14 @@ htp__ssl_thread_lock_(int mode, int type, const char * file, int line) static void htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess) { - evhtp_t * htp; - evhtp_ssl_cfg_t * cfg; - unsigned char * sid; - unsigned int slen; - - htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); - cfg = htp->ssl_cfg; + evhtp_t * htp; + evhtp_ssl_cfg_t * cfg; + evhtp_ssl_data_t * sid; + unsigned int slen; - sid = sess->session_id; - slen = sess->session_id_length; + htp = (evhtp_t *)SSL_CTX_get_app_data(ctx); + cfg = htp->ssl_cfg; + sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); if (cfg->scache_del) { @@ -2926,7 +2920,7 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; - unsigned char * sid; + evhtp_ssl_data_t * sid; int slen; connection = (evhtp_connection_t *)SSL_get_app_data(ssl); @@ -2934,10 +2928,9 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) { return 0; /* We cannot get the ssl_cfg */ } - cfg = connection->htp->ssl_cfg; - sid = sess->session_id; - slen = sess->session_id_length; + cfg = connection->htp->ssl_cfg; + sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); SSL_set_timeout(sess, cfg->scache_timeout); @@ -2950,7 +2943,7 @@ htp__ssl_add_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_sess_t * sess) } static evhtp_ssl_sess_t * -htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, unsigned char * sid, int sid_len, int * copy) +htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy) { evhtp_connection_t * connection; evhtp_ssl_cfg_t * cfg; @@ -3004,18 +2997,20 @@ htp__ssl_servername_(evhtp_ssl_t * ssl, int * unused, void * arg) if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname))) { + SSL_CTX * ctx = SSL_get_SSL_CTX(ssl); + connection->htp = evhtp_vhost; HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI); SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx); - SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx)); + SSL_set_options(ssl, SSL_CTX_get_options(ctx)); if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) || (SSL_num_renegotiations(ssl) == 0)) { - SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx), - SSL_CTX_get_verify_callback(ssl->ctx)); + SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx), + SSL_CTX_get_verify_callback(ctx)); } return SSL_TLSEXT_ERR_OK; @@ -4763,13 +4758,8 @@ evhtp_ssl_use_threads(void) int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) { -#ifdef EVHTP_ENABLE_FUTURE_STUFF - evhtp_ssl_scache_init init_cb = NULL; - evhtp_ssl_scache_add add_cb = NULL; - evhtp_ssl_scache_get get_cb = NULL; - evhtp_ssl_scache_del del_cb = NULL; -#endif - long cache_mode; + long cache_mode; + unsigned char c; if (cfg == NULL || htp == NULL || cfg->pemfile == NULL) { @@ -4853,7 +4843,12 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) if (cfg->x509_chk_issued_cb != NULL) { +#if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb; +#else + X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb); +#endif + /*SSL_CTX_set_cert_store(htp->ssl_ctx, cfg->x509_chk_issued_cb); */ } if (cfg->verify_depth) @@ -4865,29 +4860,6 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) case evhtp_ssl_scache_type_disabled: cache_mode = SSL_SESS_CACHE_OFF; break; -#ifdef EVHTP_ENABLE_FUTURE_STUFF - case evhtp_ssl_scache_type_user: - cache_mode = SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL | - SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; - - init_cb = cfg->scache_init; - add_cb = cfg->scache_add; - get_cb = cfg->scache_get; - del_cb = cfg->scache_del; - break; - case evhtp_ssl_scache_type_builtin: - cache_mode = SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL | - SSL_SESS_CACHE_NO_INTERNAL_LOOKUP; - - init_cb = htp__ssl_builtin_init_; - add_cb = htp__ssl_builtin_add_; - get_cb = htp__ssl_builtin_get_; - del_cb = htp__ssl_builtin_del_; - break; -#endif - case evhtp_ssl_scache_type_internal: default: cache_mode = SSL_SESS_CACHE_SERVER; break; diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index ae89238..e9ebde1 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -44,6 +44,11 @@ typedef SSL evhtp_ssl_t; typedef SSL_CTX evhtp_ssl_ctx_t; typedef X509 evhtp_x509_t; typedef X509_STORE_CTX evhtp_x509_store_ctx_t; +#if OPENSSL_VERSION_NUMBER < 0x10100000L +typedef unsigned char evhtp_ssl_data_t; +#else +typedef const unsigned char evhtp_ssl_data_t; +#endif #else typedef void evhtp_ssl_sess_t; typedef void evhtp_ssl_t; @@ -180,9 +185,10 @@ typedef int (* evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); typedef EVP_PKEY * (* evhtp_ssl_decrypt_cb)(char * privfile); -typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess); -typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len); -typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len); +typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess); +typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, evhtp_ssl_data_t * sid, int sid_len); +typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len); + typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif From d3da4018291f4a298a46db76e37d0cc7a97caad2 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Tue, 21 Nov 2017 14:00:48 -0500 Subject: [PATCH 08/16] remove silly comment --- evhtp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/evhtp.c b/evhtp.c index fbce474..295f864 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4841,14 +4841,12 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) X509_STORE_set_flags(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->store_flags); SSL_CTX_set_verify(htp->ssl_ctx, cfg->verify_peer, cfg->x509_verify_cb); - if (cfg->x509_chk_issued_cb != NULL) - { + if (cfg->x509_chk_issued_cb != NULL) { #if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb; #else X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb); #endif - /*SSL_CTX_set_cert_store(htp->ssl_ctx, cfg->x509_chk_issued_cb); */ } if (cfg->verify_depth) From 018dec88c43d8f15f995520c2b5abf1869d5889b Mon Sep 17 00:00:00 2001 From: Nathan French Date: Tue, 21 Nov 2017 14:03:21 -0500 Subject: [PATCH 09/16] check for errors in SSL RAND_(poll|bytes) calls --- evhtp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 295f864..8c5e0d6 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4770,7 +4770,16 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) ERR_load_crypto_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); - RAND_poll(); + + if (RAND_poll() != 1) { + log_error("RAND_poll"); + return -1; + } + + if (RAND_bytes(&c, 1) != 1) { + log_error("RAND_bytes"); + return -1; + } #if OPENSSL_VERSION_NUMBER < 0x10000000L STACK_OF(SSL_COMP) * comp_methods = SSL_COMP_get_compression_methods(); From 3467382ecc932cee7bcabb79b709c49d86a39dd9 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 27 Nov 2017 20:05:26 -0500 Subject: [PATCH 10/16] Added new virtualhost examples and functions - added examples/example_vhost.c which shows, in detail, how to use evhtp vhosts and aliases. - Added new function evhtp_add_aliases(). Much like add_alias, this allows for a variable number of aliases to be added within one call. - some error logging updates --- evhtp.c | 65 +++++++++++++++----- examples/CMakeLists.txt | 4 +- examples/example_vhost.c | 127 +++++++++++++++++++++++++++++++++++++++ include/evhtp/evhtp.h | 14 +++++ 4 files changed, 193 insertions(+), 17 deletions(-) create mode 100644 examples/example_vhost.c diff --git a/evhtp.c b/evhtp.c index 8c5e0d6..c3c1fff 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2950,12 +2950,13 @@ htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess; connection = (evhtp_connection_t * )SSL_get_app_data(ssl); + if (connection->htp == NULL) { return NULL; /* We have no way of getting ssl_cfg */ } - cfg = connection->htp->ssl_cfg; - sess = NULL; + cfg = connection->htp->ssl_cfg; + sess = NULL; if (cfg->scache_get) { @@ -4799,21 +4800,22 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts); #ifndef OPENSSL_NO_ECDH - if (cfg->named_curve != NULL) - { + if (cfg->named_curve != NULL) { EC_KEY * ecdh = NULL; int nid = 0; - nid = OBJ_sn2nid(cfg->named_curve); - if (nid == 0) - { - fprintf(stderr, "ECDH initialization failed: unknown curve %s\n", cfg->named_curve); + nid = OBJ_sn2nid(cfg->named_curve); + + if (nid == 0) { + log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve); } + ecdh = EC_KEY_new_by_curve_name(nid); - if (ecdh == NULL) - { - fprintf(stderr, "ECDH initialization failed for curve %s\n", cfg->named_curve); + + if (ecdh == NULL) { + log_error("ECDH initialization failed for curve %s", cfg->named_curve); } + SSL_CTX_set_tmp_ecdh(htp->ssl_ctx, ecdh); EC_KEY_free(ecdh); } @@ -4825,6 +4827,7 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) DH * dh; fh = fopen(cfg->dhparams, "r"); + if (fh != NULL) { dh = PEM_read_DHparams(fh, NULL, NULL, NULL); @@ -4833,17 +4836,21 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) SSL_CTX_set_tmp_dh(htp->ssl_ctx, dh); DH_free(dh); } else { - fprintf(stderr, "DH initialization failed: unable to parse file %s\n", cfg->dhparams); + log_error("DH initialization failed: unable to parse file %s", cfg->dhparams); } + fclose(fh); } else { - fprintf(stderr, "DH initialization failed: unable to open file %s\n", cfg->dhparams); + log_error("DH initialization failed: unable to open file %s", cfg->dhparams); } } #endif /* OPENSSL_NO_DH */ if (cfg->ciphers != NULL) { - SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers); + if (SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers) == 0) { + log_error("set_cipher_list"); + return -1; + } } SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, cfg->capath); @@ -4858,8 +4865,7 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) #endif } - if (cfg->verify_depth) - { + if (cfg->verify_depth) { SSL_CTX_set_verify_depth(htp->ssl_ctx, cfg->verify_depth); } @@ -5158,6 +5164,8 @@ evhtp_add_alias(evhtp_t * evhtp, const char * name) return -1; } + log_debug("Adding %s to aliases", name); + alias->alias = htp__strdup_(name); evhtp_alloc_assert(alias->alias); @@ -5166,6 +5174,31 @@ evhtp_add_alias(evhtp_t * evhtp, const char * name) return 0; } +int +evhtp_add_aliases(evhtp_t * htp, const char * name, ...) { + va_list argp; + size_t len; + + if (evhtp_add_alias(htp, name) == -1) { + return -1; + } + + va_start(argp, name); + { + const char * p; + + while ((p = va_arg(argp, const char *)) != NULL) { + if (evhtp_add_alias(htp, p) == -1) { + log_error("Unable to add %s alias", p); + return -1; + } + } + } + va_end(argp); + + return 0; +} + int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost) { diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index c69aac5..d3a60c7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable(test_vhost EXCLUDE_FROM_ALL test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL test_client.c) add_executable(test_query EXCLUDE_FROM_ALL test_query.c) add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c) +add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c) if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c) @@ -19,7 +20,8 @@ target_link_libraries(test_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -add_dependencies(examples test_extensive test_basic test_vhost test_client test_query test_perf) +add_dependencies(examples example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/example_vhost.c b/examples/example_vhost.c new file mode 100644 index 0000000..4a61003 --- /dev/null +++ b/examples/example_vhost.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include + +#include "internal.h" +#include "../log.h" +#include "evhtp/evhtp.h" + +#define make_response(cb) do { \ + evbuffer_add_printf(req->buffer_out, \ + "%s = host:%s, arg:%s\n", cb, \ + evhtp_header_find(req->headers_in, "Host"), \ + (char *)arg); \ +} while (0) + + +static void +vhost_1__callback_(evhtp_request_t * req, void * arg) { + /* these should be our callbacks for our evhtp.io hosts */ + make_response("vhost_1__callback_"); + + evhtp_send_reply(req, EVHTP_RES_OK); +} + +static void +vhost_2__callback_(evhtp_request_t * req, void * arg) { + /* these should be our callbacks for our google hosts */ + make_response("vhost_2__callback_"); + + evhtp_send_reply(req, EVHTP_RES_OK); +} + +int +main(int argc, char ** argv) { + struct event_base * evbase; + evhtp_t * htp; + evhtp_t * htp_vhost_1; + evhtp_t * htp_vhost_2; + + evbase = event_base_new(); + evhtp_alloc_assert(evbase); + + /* allocate our main evhtp structure which vhosts are + * nested under + */ + htp = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp); + + /* create a evhtp structure for vhost_1 specific hostnames, + * this will match hostnames for 'evhtp.io' + * */ + htp_vhost_1 = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp_vhost_1); + + /* running a get on /vhost for htp_vhost_1 will be different + * from htp_vhost_2 due to the hostname we set below. + */ + evhtp_set_cb(htp_vhost_1, "/vhost", vhost_1__callback_, "evhtp.io domains"); + + /* create a evhtp structure for vhost_2 specific hostnames, + * this will match hostnames for 'google.com' + */ + htp_vhost_2 = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp_vhost_2); + + /* running a get on /vhost for http_vhost_2 will be different + * from the http_vhost_1 due to the hostname we set below. + */ + evhtp_set_cb(htp_vhost_2, "/vhost", vhost_2__callback_, "google.com domains"); + + /* if Host: evhtp.io is present, the callbacks fro htp_vhost_1 are + * used. We do this by adding the vhost_1 evhtp to the main htp ctx. + */ + evhtp_add_vhost(htp, "evhtp.io", htp_vhost_1); + + /* now lets set some virtual host aliases to evhtp.io */ + evhtp_add_aliases(htp_vhost_1, + "www.evhtp.io", + "web.evhtp.io", NULL); + + /* If Host: google.com is present, the callbacks for htp_vhost_2 are + * used instead. This must be attached to the main htp context. + */ + evhtp_add_vhost(htp, "google.com", htp_vhost_2); + + /* now add some virtual host aliases for google.com */ + evhtp_add_aliases(htp_vhost_2, + "www.google.com", + "web.google.com", + "inbox.google.com", NULL); + + /* we can also append a single alias to vhost_2 like this */ + evhtp_add_alias(htp_vhost_2, "gmail.google.com"); + + /* now bind and listen on our server */ + evhtp_bind_socket(htp, "127.0.0.1", 0, 128); + + { + struct sockaddr_in sin; + socklen_t len = sizeof(struct sockaddr); + uint16_t port; + + getsockname( + evconnlistener_get_fd(htp->server), + (struct sockaddr *)&sin, &len); + + port = ntohs(sin.sin_port); + + log_info("[[ try the following commands and you should see 'evhtp.io domains' ]]"); + log_info("====================================================================="); + log_info("curl -H'Host: evhtp.io' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: www.evhtp.io' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: web.evhtp.io' http://127.0.0.1:%d/vhost", port); + log_info("========================================================================"); + log_info("[[ try the following commands and you should see 'google.com domains' ]]"); + log_info("========================================================================"); + log_info("curl -H'Host: google.com' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: www.google.com' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: web.google.com' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: inbox.google.com' http://127.0.0.1:%d/vhost", port); + log_info("curl -H'Host: gmail.google.com' http://127.0.0.1:%d/vhost", port); + } + + event_base_loop(evbase, 0); +} /* main */ diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index e9ebde1..6b31e24 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -988,6 +988,20 @@ EVHTP_EXPORT int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * v */ EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name); + +/** + * @brief set a variable number of aliases in one call + * @reference evhtp_add_alias + * @note last argument must be NULL terminated + * + * @param evhtp + * @param name + * @param ... + * + * @return 0 on success, -1 on error + */ +EVHTP_EXPORT int evhtp_add_aliases(evhtp_t * evhtp, const char * name, ...); + /** * @brief Allocates a new key/value structure. * From 282a1c93a60429b4a0f6433fc5e7c7271e7ca49b Mon Sep 17 00:00:00 2001 From: Nathan French Date: Thu, 30 Nov 2017 10:40:34 -0500 Subject: [PATCH 11/16] added example request pausing app --- examples/CMakeLists.txt | 4 +- examples/example_pause.c | 125 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 examples/example_pause.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d3a60c7..76ad4d0 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -7,6 +7,7 @@ add_executable(test_client EXCLUDE_FROM_ALL test_client.c) add_executable(test_query EXCLUDE_FROM_ALL test_query.c) add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c) add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c) +add_executable(example_pause EXCLUDE_FROM_ALL example_pause.c) if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c) @@ -21,7 +22,8 @@ target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(example_pause evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -add_dependencies(examples example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) +add_dependencies(examples example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/example_pause.c b/examples/example_pause.c new file mode 100644 index 0000000..247a7da --- /dev/null +++ b/examples/example_pause.c @@ -0,0 +1,125 @@ +/* + * Quick example of how to pause a request, and in this case, simply + * set a timer to emit the response 10 seconds later. + * + * This is a good way to long running tasks before responding (thus + * not blocking any other processing). + */ +#include +#include +#include +#include +#include + +#include "../log.h" +#include "internal.h" +#include "evhtp/evhtp.h" + +struct paused_request_ { + struct event * _timeoutev; + struct timeval _timeout; + evhtp_request_t * _request; +}; + +/* once 10 seconds has passed, this function is called, it will + * resume the request, and send the final response back to the + * client. + */ +static void +http_resume__callback_(int sock, short events, void * arg) { + struct paused_request_ * preq; + evhtp_request_t * req; + + evhtp_assert(arg != NULL); + + preq = (struct paused_request_ *)arg; + req = preq->_request; + evhtp_assert(req != NULL); + + event_free(preq->_timeoutev); + free(preq); + + /* add the current time to our output buffer to the client */ + evbuffer_add_printf(req->buffer_out, "time end %ld\n", time(NULL)); + + /* inform the evhtp API to resume this connection request */ + evhtp_request_resume(req); + + /* finally send the response to the client, YAY! */ + evhtp_send_reply(req, EVHTP_RES_OK); +} + +/* this is our default callback, it is the one who sets up the evtimer + * that triggers the response after 10 seconds. + */ +static void +http_pause__callback_(evhtp_request_t * req, void * arg) { + struct timeval * tv = (struct timeval *)arg; + struct paused_request_ * preq; + + /* allocate a little structure that holds our evtimer and the + * pending request, se the timeout to 10 seconds. + */ + preq = malloc(sizeof(*preq)); + evhtp_alloc_assert(preq); + + preq->_request = req; + preq->_timeout.tv_sec = tv->tv_sec; + preq->_timeout.tv_usec = tv->tv_usec; + + /* when 10 seconds is up, the function http_resume__callback_ will + * be called, this function will actually send the response. + */ + preq->_timeoutev = evtimer_new(req->htp->evbase, http_resume__callback_, preq); + evhtp_alloc_assert(preq->_timeoutev); + + /* just for debugging, add the time the request was first seen */ + evbuffer_add_printf(req->buffer_out, "time start %ld\n", time(NULL)); + + /* add the timer to the event loop */ + evtimer_add(preq->_timeoutev, &preq->_timeout); + + /* notify the evhtp API to "pause" this request (meaning it will no + * longer do any work on this connection until it is "resumed". + */ + evhtp_request_pause(req); +} + +int +main(int argc, char ** argv) { + evhtp_t * htp; + struct event_base * evbase; + struct timeval timeo = { 10, 0 }; + + evbase = event_base_new(); + evhtp_alloc_assert(evbase); + + htp = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp); + + /* we just set the default callback for any requests to + * the function that pauses the session, sets a timer, + * and 10 seconds later, sends the response. + */ + evhtp_set_gencb(htp, http_pause__callback_, &timeo); + + evhtp_bind_socket(htp, "127.0.0.1", 0, 128); + { + struct sockaddr_in sin; + socklen_t len = sizeof(struct sockaddr); + uint16_t port; + + getsockname( + evconnlistener_get_fd(htp->server), + (struct sockaddr *)&sin, &len); + + port = ntohs(sin.sin_port); + + log_info("request will be delayed for 10 seconds with: curl http://127.0.0.1:%d/", port); + } + + event_base_loop(evbase, 0); + + + return 0; +} From 1e0c241b515a40d68d6580e58f0b089fbea35ad3 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 15:58:18 -0500 Subject: [PATCH 12/16] Added a more extensive SSL sandbox. After running `make examples`, if SSL is enabled, you can quickly test HTTPS, with optional client-based certificate authentication using the following process within the build directory: ``` ./examples/https/bin/generate.sh -- Test without client auth ./examples/example_https \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem curl -vk https://localhost:4443/ -- Test WITH client auth ./examples/example_https \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -ca examples/https/ca-crt.pem \ -verify-peer \ -verify-depth 2 \ -enforce-peer-cert curl -kv \ --key examples/https/client1-key.pem \ --cert examples/https/client1-crt.pem \ https://localhost:4443/ ``` --- examples/CMakeLists.txt | 12 +- examples/https/bin/generate.sh.in | 58 +++++++ examples/https/etc/ca.cnf | 31 ++++ examples/https/etc/client1.cnf | 26 +++ examples/https/etc/client2.cnf | 26 +++ examples/https/etc/server.cnf | 26 +++ examples/https/example_https.c | 256 ++++++++++++++++++++++++++++++ 7 files changed, 434 insertions(+), 1 deletion(-) create mode 100755 examples/https/bin/generate.sh.in create mode 100644 examples/https/etc/ca.cnf create mode 100644 examples/https/etc/client1.cnf create mode 100644 examples/https/etc/client2.cnf create mode 100644 examples/https/etc/server.cnf create mode 100644 examples/https/example_https.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 76ad4d0..31c3b8b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(test_query EXCLUDE_FROM_ALL test_query.c) add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c) add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c) add_executable(example_pause EXCLUDE_FROM_ALL example_pause.c) +add_executable(example_https EXCLUDE_FROM_ALL https/example_https.c) if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c) @@ -23,7 +24,16 @@ target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(example_pause evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(example_https evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -add_dependencies(examples example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) +add_dependencies(examples example_https example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) +file (COPY + https/etc/ca.cnf + https/etc/client1.cnf + https/etc/client2.cnf + https/etc/server.cnf + DESTINATION + https/etc/) +configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY) diff --git a/examples/https/bin/generate.sh.in b/examples/https/bin/generate.sh.in new file mode 100755 index 0000000..6f78154 --- /dev/null +++ b/examples/https/bin/generate.sh.in @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +CONFIG_DIR="@PROJECT_BINARY_DIR@/examples/https" + +# Create new CA +openssl req -new -x509 -days 9999 \ + -config "$CONFIG_DIR/etc/ca.cnf" \ + -keyout "$CONFIG_DIR/ca-key.pem" \ + -out "$CONFIG_DIR/ca-crt.pem" + +# Generate private key for server +openssl genrsa -out "$CONFIG_DIR/server-key.pem" 4096 + +# Generate cert signing request +openssl req -new \ + -config "$CONFIG_DIR/etc/server.cnf" \ + -key "$CONFIG_DIR/server-key.pem" \ + -out "$CONFIG_DIR/server-csr.pem" + +# Sign the request +openssl x509 -req \ + -extfile "$CONFIG_DIR/etc/server.cnf" \ + -days 999 \ + -passin "pass:password" \ + -in "$CONFIG_DIR/server-csr.pem" \ + -CA "$CONFIG_DIR/ca-crt.pem" \ + -CAkey "$CONFIG_DIR/ca-key.pem" \ + -CAcreateserial \ + -out "$CONFIG_DIR/server-crt.pem" + +# Generate a few client certs +openssl genrsa -out "$CONFIG_DIR/client1-key.pem" 4096 +openssl genrsa -out "$CONFIG_DIR/client2-key.pem" 4096 + +# create two cert sign requests +openssl req -new -config "$CONFIG_DIR/etc/client1.cnf" -key $CONFIG_DIR/client1-key.pem -out $CONFIG_DIR/client1-csr.pem +openssl req -new -config $CONFIG_DIR/etc/client2.cnf -key $CONFIG_DIR/client2-key.pem -out $CONFIG_DIR/client2-csr.pem + +# sign the above client certs +openssl x509 -req \ + -extfile $CONFIG_DIR/etc/client1.cnf \ + -days 999 \ + -passin "pass:password" \ + -in $CONFIG_DIR/client1-csr.pem \ + -CA $CONFIG_DIR/ca-crt.pem \ + -CAkey $CONFIG_DIR/ca-key.pem \ + -CAcreateserial \ + -out $CONFIG_DIR/client1-crt.pem + +openssl x509 -req \ + -extfile $CONFIG_DIR/etc/client2.cnf \ + -days 999 \ + -passin "pass:password" \ + -in $CONFIG_DIR/client2-csr.pem \ + -CA $CONFIG_DIR/ca-crt.pem \ + -CAkey $CONFIG_DIR/ca-key.pem \ + -CAcreateserial \ + -out $CONFIG_DIR/client2-crt.pem diff --git a/examples/https/etc/ca.cnf b/examples/https/etc/ca.cnf new file mode 100644 index 0000000..53eab64 --- /dev/null +++ b/examples/https/etc/ca.cnf @@ -0,0 +1,31 @@ +[ ca ] +default_ca = CA_default + +[ CA_default ] +serial = ca-serial +crl = ca-crl.pem +database = ca-database.txt +name_opt = CA_default +cert_opt = CA_default +default_crl_days = 9999 +default_md = md5 + +[ req ] +default_bits = 4096 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +output_password = password + +[ req_distinguished_name ] +C = US +ST = MA +L = Boston +O = Critical Stack +OU = evhtp +CN = ca +emailAddress = nate@cl0d.com + +[ req_attributes ] +challengePassword = test diff --git a/examples/https/etc/client1.cnf b/examples/https/etc/client1.cnf new file mode 100644 index 0000000..6e881ff --- /dev/null +++ b/examples/https/etc/client1.cnf @@ -0,0 +1,26 @@ +[ req ] +default_bits = 4096 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +x509_extensions = v3_ca + +[ req_distinguished_name ] +C = US +ST = MA +L = Boston +O = Critical Stack +OU = evhtp +CN = client1 +emailAddress = nate@cl0d.com + +[ req_attributes ] +challengePassword = password + +[ v3_ca ] +authorityInfoAccess = @issuer_info + +[ issuer_info ] +OCSP;URI.0 = http://ocsp.example.com/ +caIssuers;URI.0 = http://example.com/ca.cert diff --git a/examples/https/etc/client2.cnf b/examples/https/etc/client2.cnf new file mode 100644 index 0000000..2cbf570 --- /dev/null +++ b/examples/https/etc/client2.cnf @@ -0,0 +1,26 @@ +[ req ] +default_bits = 4096 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +x509_extensions = v3_ca + +[ req_distinguished_name ] +C = US +ST = MA +L = Boston +O = Critical Stack +OU = evhtp +CN = client2 +emailAddress = nate@cl0d.com + +[ req_attributes ] +challengePassword = password + +[ v3_ca ] +authorityInfoAccess = @issuer_info + +[ issuer_info ] +OCSP;URI.0 = http://ocsp.example.com/ +caIssuers;URI.0 = http://example.com/ca.cert diff --git a/examples/https/etc/server.cnf b/examples/https/etc/server.cnf new file mode 100644 index 0000000..2c36ee4 --- /dev/null +++ b/examples/https/etc/server.cnf @@ -0,0 +1,26 @@ +[ req ] +default_bits = 4096 +days = 9999 +distinguished_name = req_distinguished_name +attributes = req_attributes +prompt = no +x509_extensions = v3_ca + +[ req_distinguished_name ] +C = US +ST = MA +L = Boston +O = Critical Stack +OU = evhtp +CN = localhost +emailAddress = nate@cl0d.com + +[ req_attributes ] +challengePassword = password + +[ v3_ca ] +authorityInfoAccess = @issuer_info + +[ issuer_info ] +OCSP;URI.0 = http://ocsp.example.com/ +caIssuers;URI.0 = http://example.com/ca.cert diff --git a/examples/https/example_https.c b/examples/https/example_https.c new file mode 100644 index 0000000..f39105b --- /dev/null +++ b/examples/https/example_https.c @@ -0,0 +1,256 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../log.h" +#include "internal.h" +#include "evhtp/evhtp.h" + +static void +http__callback_(evhtp_request_t * req, void * arg) { + return evhtp_send_reply(req, EVHTP_RES_OK); +} + +static int +ssl__x509_verify_(int ok, X509_STORE_CTX * store) { + char buf[256]; + X509 * err_cert; + int err; + int depth; + SSL * ssl; + evhtp_connection_t * connection; + evhtp_ssl_cfg_t * ssl_cfg; + + err_cert = X509_STORE_CTX_get_current_cert(store); + err = X509_STORE_CTX_get_error(store); + depth = X509_STORE_CTX_get_error_depth(store); + ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()); + + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + + connection = SSL_get_app_data(ssl); + ssl_cfg = connection->htp->ssl_cfg; + + if (depth > ssl_cfg->verify_depth) { + ok = 0; + err = X509_V_ERR_CERT_CHAIN_TOO_LONG; + + X509_STORE_CTX_set_error(store, err); + } + + if (!ok) { + log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err, + X509_verify_cert_error_string(err), depth, buf); + } + + return ok; +} + +enum { + OPTARG_CERT = 1000, + OPTARG_KEY, + OPTARG_CA, + OPTARG_CAPATH, + OPTARG_CIPHERS, + OPTARG_VERIFY_PEER, + OPTARG_ENFORCE_PEER_CERT, + OPTARG_VERIFY_DEPTH, + OPTARG_ENABLE_CACHE, + OPTARG_CACHE_TIMEOUT, + OPTARG_CACHE_SIZE, + OPTARG_CTX_TIMEOUT, + OPTARG_ENABLE_PROTOCOL, + OPTARG_DISABLE_PROTOCOL +}; + +static const char * help = + "Usage %s [opts] :\n" + " -cert : Server PEM-encoded X.509 Certificate file\n" + " -key : Server PEM-encoded Private Key file\n" + " -ca : File of PEM-encoded Server CA Certificates\n" + " -capath : Directory of PEM-encoded CA Certificates for Client Auth\n" + " -ciphers : Accepted SSL Ciphers\n" + " -verify-peer : Enable SSL client verification\n" + " -enforce-peer-cert : Reject clients without a cert\n" + " -verify-depth : Maximum depth of CA Certificates in Client Certificate verification\n" + " -enable-protocol

: Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" + " -disable-protocol

: Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n" + " -ctx-timeout : SSL Session Timeout (SSL >= 1.0)\n"; + +evhtp_ssl_cfg_t * +parse__ssl_opts_(int argc, char ** argv) { + int opt = 0; + int long_index = 0; + int ssl_verify_mode = 0; + struct stat f_stat; + evhtp_ssl_cfg_t * ssl_config = calloc(1, sizeof(evhtp_ssl_cfg_t)); + + + ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; + + static struct option long_options[] = { + { "cert", required_argument, 0, OPTARG_CERT }, + { "key", required_argument, 0, OPTARG_KEY }, + { "ca", required_argument, 0, OPTARG_CA }, + { "capath", required_argument, 0, OPTARG_CAPATH }, + { "ciphers", required_argument, 0, OPTARG_CIPHERS }, + { "verify-peer", no_argument, 0, OPTARG_VERIFY_PEER }, + { "enforce-peer-cert", no_argument, 0, OPTARG_ENFORCE_PEER_CERT }, + { "verify-depth", required_argument, 0, OPTARG_VERIFY_DEPTH }, + { "enable-cache", no_argument, 0, OPTARG_ENABLE_CACHE }, + { "cache-timeout", required_argument, 0, OPTARG_CACHE_TIMEOUT }, + { "cache-size", required_argument, 0, OPTARG_CACHE_SIZE }, + { "enable-protocol", required_argument, 0, OPTARG_ENABLE_PROTOCOL }, + { "disable-protocol", required_argument, 0, OPTARG_DISABLE_PROTOCOL }, + { "ctx-timeout", required_argument, 0, OPTARG_CTX_TIMEOUT }, + { "help", no_argument, 0, 'h' }, + { NULL, 0, 0, 0 } + }; + + while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) { + switch (opt) { + case 'h': + printf(help, argv[0]); + exit(EXIT_FAILURE); + case OPTARG_CERT: + ssl_config->pemfile = strdup(optarg); + break; + case OPTARG_KEY: + ssl_config->privfile = strdup(optarg); + break; + case OPTARG_CA: + ssl_config->cafile = strdup(optarg); + break; + case OPTARG_CAPATH: + ssl_config->capath = strdup(optarg); + break; + case OPTARG_CIPHERS: + ssl_config->ciphers = strdup(optarg); + break; + case OPTARG_VERIFY_DEPTH: + ssl_config->verify_depth = atoi(optarg); + break; + case OPTARG_VERIFY_PEER: + ssl_verify_mode |= SSL_VERIFY_PEER; + break; + case OPTARG_ENFORCE_PEER_CERT: + ssl_verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + break; + case OPTARG_ENABLE_CACHE: + ssl_config->scache_type = evhtp_ssl_scache_type_internal; + break; + case OPTARG_CACHE_TIMEOUT: + ssl_config->scache_timeout = atoi(optarg); + break; + case OPTARG_CACHE_SIZE: + ssl_config->scache_size = atoi(optarg); + break; + case OPTARG_CTX_TIMEOUT: + ssl_config->ssl_ctx_timeout = atoi(optarg); + break; + case OPTARG_ENABLE_PROTOCOL: + if (!strcasecmp(optarg, "SSLv2")) { + ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2; + } else if (!strcasecmp(optarg, "SSLv3")) { + ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3; + } else if (!strcasecmp(optarg, "TLSv1")) { + ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1; + } else if (!strcasecmp(optarg, "ALL")) { + ssl_config->ssl_opts = 0; + } + + break; + case OPTARG_DISABLE_PROTOCOL: + if (!strcasecmp(optarg, "SSLv2")) { + ssl_config->ssl_opts |= SSL_OP_NO_SSLv2; + } else if (!strcasecmp(optarg, "SSLv3")) { + ssl_config->ssl_opts |= SSL_OP_NO_SSLv3; + } else if (!strcasecmp(optarg, "TLSv1")) { + ssl_config->ssl_opts |= SSL_OP_NO_TLSv1; + } else if (!strcasecmp(optarg, "ALL")) { + ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1; + } + break; + + default: + break; + } /* switch */ + } + + if (ssl_verify_mode != 0) { + ssl_config->verify_peer = ssl_verify_mode; + ssl_config->x509_verify_cb = ssl__x509_verify_; + } + + + if (ssl_config->pemfile) { + if (stat(ssl_config->pemfile, &f_stat) != 0) { + log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (ssl_config->privfile) { + if (stat(ssl_config->privfile, &f_stat) != 0) { + log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (ssl_config->cafile) { + if (stat(ssl_config->cafile, &f_stat) != 0) { + log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (ssl_config->capath) { + if (stat(ssl_config->capath, &f_stat) != 0) { + log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno)); + exit(EXIT_FAILURE); + } + } + + return ssl_config; +} /* parse__ssl_opts_ */ + +int +main(int argc, char ** argv) { + evhtp_t * htp; + struct event_base * evbase; + + evbase = event_base_new(); + evhtp_alloc_assert(evbase); + + htp = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp); + + evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv)); + evhtp_set_gencb(htp, http__callback_, NULL); + + evhtp_bind_socket(htp, "127.0.0.1", 4443, 128); + { + struct sockaddr_in sin; + socklen_t len = sizeof(struct sockaddr); + uint16_t port; + + getsockname( + evconnlistener_get_fd(htp->server), + (struct sockaddr *)&sin, &len); + + port = ntohs(sin.sin_port); + + log_info("curl https://127.0.0.1:%d/", port); + } + + event_base_loop(evbase, 0); + + + return 0; +} From 6ecf7e7dbb4ff28c02967aa75d50d3595cfdfb34 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 16:11:01 -0500 Subject: [PATCH 13/16] Add examples/https/README --- examples/https/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/https/README.md diff --git a/examples/https/README.md b/examples/https/README.md new file mode 100644 index 0000000..0e1b539 --- /dev/null +++ b/examples/https/README.md @@ -0,0 +1,32 @@ +After running `make examples`, if SSL is enabled, you can quickly test HTTPS, with optional client-based certificate authentication using the following process within the build directory: + +``` +# do all the stupid ssl generation +./examples/https/bin/generate.sh + +# Test without client auth + +# Run the server +./examples/example_https \ + -cert examples/https/server-crt.pem \ + -key examples/https/server-key.pem + +# Make a request +curl -vk https://localhost:4443/ + +# Test WITH client auth + +./examples/example_https \ + -cert examples/https/server-crt.pem \ + -key examples/https/server-key.pem \ + -ca examples/https/ca-crt.pem \ + -verify-peer \ + -verify-depth 2 \ + -enforce-peer-cert + +# Make a request with the client key +curl -kv \ + --key examples/https/client1-key.pem \ + --cert examples/https/client1-crt.pem \ + https://localhost:4443/ +``` From 0848e08abafca860a0c9f3f877e9bcc040e867ff Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 16:17:13 -0500 Subject: [PATCH 14/16] exit failure in example_https when SSL is disabled --- examples/https/example_https.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/examples/https/example_https.c b/examples/https/example_https.c index f39105b..18a4723 100644 --- a/examples/https/example_https.c +++ b/examples/https/example_https.c @@ -12,6 +12,7 @@ #include "internal.h" #include "evhtp/evhtp.h" +#ifndef EVHTP_DISABLE_SSL static void http__callback_(evhtp_request_t * req, void * arg) { return evhtp_send_reply(req, EVHTP_RES_OK); @@ -219,9 +220,11 @@ parse__ssl_opts_(int argc, char ** argv) { return ssl_config; } /* parse__ssl_opts_ */ +#endif int main(int argc, char ** argv) { +#ifndef EVHTP_DISABLE_SSL evhtp_t * htp; struct event_base * evbase; @@ -250,7 +253,9 @@ main(int argc, char ** argv) { } event_base_loop(evbase, 0); - - return 0; +#else + log_error("Not compiled with SSL support, go away"); + return EXIT_FAILURE; +#endif } From 4c4eb3ac7e161c4ceed0717f02ce146a2d49e133 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 16:23:29 -0500 Subject: [PATCH 15/16] [#26] Use SSL_CTX_use_certificate_chain_file (reference https://github.com/criticalstack/libevhtp/issues/26) Via @mannol ``` Currently, libevhtp is using SSL_CTX_use_certificate_file to load a certificate file. That function lacks the ability to load the pinned certificate chain (if any) which has a consequence of connecting clients not trusting the received certificate. By using SSL_CTX_use_certificate_chain_file we give the libssl the ability to read and send the entire certificate chain (if any), which clients can check against. ``` --- evhtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index c3c1fff..d9e1bb1 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4878,7 +4878,7 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) break; } /* switch */ - SSL_CTX_use_certificate_file(htp->ssl_ctx, cfg->pemfile, SSL_FILETYPE_PEM); + SSL_CTX_use_certificate_chain_file(htp->ssl_ctx, cfg->pemfile); char * const key = cfg->privfile ? cfg->privfile : cfg->pemfile; From 564d69439cb66216cb26b044dee5492ad6dc7e63 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 17:34:10 -0500 Subject: [PATCH 16/16] Release v1.2.15, see ChangeLog for details --- ChangeLog | 13 +++++++++++++ cmake/version.cmake | 2 +- include/evhtp/evhtp.h | 4 ++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1f03277..8ada5aa 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +v1.2.15 + o deprecated unset_hook and set_hook / cleanup (f1d2bd1 Nathan French) + o updated travis configuration (45003e1 Nathan French) + o Latest revision to support multiple SSL versions (4e353ba Tony Lambiris) + o remove silly comment (d3da401 Nathan French) + o check for errors in SSL RAND_(poll|bytes) calls (018dec8 Nathan French) + o Added new virtualhost examples and functions (3467382 Nathan French) + o added example request pausing app (282a1c9 Nathan French) + o Added a more extensive SSL sandbox. (1e0c241 Nathan French) + o Add examples/https/README (6ecf7e7 Nathan French) + o exit failure in example_https when SSL is disabled (0848e08 Nathan French) + o [#26] Use SSL_CTX_use_certificate_chain_file (4c4eb3a Nathan French) + v1.2.14 - !!!! SECURITY UPDATE !!!! o Added doxygen tags. Updated Doxyfile to include more sources and headers. (50ab41d Dan Henderson) o SSL logging on handshake errors (0fff6bc Nathan French) diff --git a/cmake/version.cmake b/cmake/version.cmake index d7c14ed..f88c98f 100644 --- a/cmake/version.cmake +++ b/cmake/version.cmake @@ -1,5 +1,5 @@ set (PROJECT_MAJOR_VERSION 1) set (PROJECT_MINOR_VERSION 2) -set (PROJECT_PATCH_VERSION 14) +set (PROJECT_PATCH_VERSION 15) set (PROJECT_VERSION "${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION}") diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index 6b31e24..f7cd3d9 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -192,10 +192,10 @@ typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connect typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif -#define EVHTP_VERSION "1.2.14" +#define EVHTP_VERSION "1.2.15" #define EVHTP_VERSION_MAJOR 1 #define EVHTP_VERSION_MINOR 2 -#define EVHTP_VERSION_PATCH 14 +#define EVHTP_VERSION_PATCH 15 #define evhtp_headers_iterator evhtp_kvs_iterator