From ab190e1687fef2e82bfdb09d810a2c5bda3a103a Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 6 Dec 2017 17:16:46 -0500 Subject: [PATCH 01/19] Added various SSL utility functions These new functions are mostly SSL type objects that converted into ASCII strings (which much be free()'d) - htp_sslutil_subject_tostr() : creates string from the ssl subject - htp_sslutil_issuer_tostr() - htp_sslutil_notbefore_tostr() - htp_sslutil_notafter_tostr() - htp_sslutil_sha1_tostr() - htp_sslutil_serial_tostr() - htp_sslutil_cipher_tostr() - htp_sslutil_cert_tostr() - htp_sslutil_x509_ext_tostr() --- CMakeLists.txt | 3 +- sslutils.c | 463 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 465 insertions(+), 1 deletion(-) create mode 100644 sslutils.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3617d97..fac0450 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,7 +185,8 @@ set(LIBEVHTP_EXTERNAL_LIBS set (LIBEVHTP_SOURCE_FILES evhtp.c numtoa.c - parser.c) + parser.c + sslutils.c) if (NOT EVHTP_DISABLE_EVTHR) list (APPEND LIBEVHTP_SOURCE_FILES thread.c) diff --git a/sslutils.c b/sslutils.c new file mode 100644 index 0000000..7078efb --- /dev/null +++ b/sslutils.c @@ -0,0 +1,463 @@ +#include +#include +#include +#include +#include +#include + +#include "evhtp/config.h" +#include "evhtp/evhtp.h" +#include "internal.h" + +unsigned char * +htp_sslutil_subject_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + unsigned char * subj_str; + char * p; + X509 * cert; + X509_NAME * name; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(name = X509_get_subject_name(cert))) { + X509_free(cert); + return NULL; + } + + if (!(p = X509_NAME_oneline(name, NULL, 0))) { + X509_free(cert); + return NULL; + } + + subj_str = strdup(p); + + OPENSSL_free(p); + X509_free(cert); + + return subj_str; +#else + return NULL; +#endif +} + +unsigned char * +htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + X509 * cert; + X509_NAME * name; + char * p; + unsigned char * issr_str; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(name = X509_get_issuer_name(cert))) { + X509_free(cert); + return NULL; + } + + if (!(p = X509_NAME_oneline(name, NULL, 0))) { + X509_free(cert); + return NULL; + } + + issr_str = strdup(p); + + OPENSSL_free(p); + X509_free(cert); + + return issr_str; +#else + return NULL; +#endif +} + +unsigned char * +htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + BIO * bio; + X509 * cert; + ASN1_TIME * time; + size_t len; + unsigned char * time_str; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(time = X509_get_notBefore(cert))) { + X509_free(cert); + return NULL; + } + + if (!(bio = BIO_new(BIO_s_mem()))) { + X509_free(cert); + return NULL; + } + + if (!ASN1_TIME_print(bio, time)) { + BIO_free(bio); + X509_free(cert); + return NULL; + } + + if ((len = BIO_pending(bio)) == 0) { + BIO_free(bio); + X509_free(cert); + return NULL; + } + + if (!(time_str = calloc(len + 1, 1))) { + return NULL; + } + + BIO_read(bio, time_str, len); + + BIO_free(bio); + X509_free(cert); + + return time_str; +#else + return NULL; +#endif +} /* htp_sslutil_notbefore_tostr */ + +unsigned char * +htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + BIO * bio; + X509 * cert; + ASN1_TIME * time; + size_t len; + unsigned char * time_str; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(time = X509_get_notAfter(cert))) { + X509_free(cert); + return NULL; + } + + if (!(bio = BIO_new(BIO_s_mem()))) { + X509_free(cert); + return NULL; + } + + if (!ASN1_TIME_print(bio, time)) { + BIO_free(bio); + X509_free(cert); + return NULL; + } + + if ((len = BIO_pending(bio)) == 0) { + BIO_free(bio); + X509_free(cert); + return NULL; + } + + if (!(time_str = calloc(len + 1, 1))) { + return NULL; + } + + BIO_read(bio, time_str, len); + + BIO_free(bio); + X509_free(cert); + + return time_str; +#else + return NULL; +#endif +} /* htp_sslutil_notafter_tostr */ + +unsigned char * +htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + const EVP_MD * md_alg; + X509 * cert; + unsigned int n; + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned char * buf = NULL; + size_t offset; + size_t nsz; + int sz; + int i; + + if (!ssl) { + return NULL; + } + + md_alg = EVP_sha1(); + + if (!md_alg) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + n = 0; + if (!X509_digest(cert, md_alg, md, &n)) { + return NULL; + } + + nsz = 3 * n + 1; + buf = (unsigned char *)calloc(nsz, 1); + if (buf) { + offset = 0; + for (i = 0; i < n; i++) { + sz = snprintf(buf + offset, nsz - offset, "%02X%c", md[i], (i + 1 == n) ? 0 : ':'); + offset += sz; + + if (sz < 0 || offset >= nsz) { + free(buf); + buf = NULL; + break; + } + } + } + + X509_free(cert); + + return buf; +#else + return NULL; +#endif +} /* htp_sslutil_sha1_tostr */ + +unsigned char * +htp_sslutil_serial_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + BIO * bio; + X509 * cert; + size_t len; + unsigned char * ser_str; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(bio = BIO_new(BIO_s_mem()))) { + X509_free(cert); + return NULL; + } + + i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert)); + + if ((len = BIO_pending(bio)) == 0) { + BIO_free(bio); + X509_free(cert); + return NULL; + } + + if (!(ser_str = calloc(len + 1, 1))) { + return NULL; + } + + BIO_read(bio, ser_str, len); + + X509_free(cert); + BIO_free(bio); + + return ser_str; +#else + return NULL; +#endif +} /* htp_sslutil_serial_tostr */ + +unsigned char * +htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + const SSL_CIPHER * cipher; + const char * p; + unsigned char * cipher_str; + + if (!ssl) { + return NULL; + } + + if (!(cipher = SSL_get_current_cipher(ssl))) { + return NULL; + } + + if (!(p = SSL_CIPHER_get_name(cipher))) { + return NULL; + } + + cipher_str = strdup(p); + + return cipher_str; +#else + return NULL; +#endif +} + +unsigned char * +htp_sslutil_cert_tostr(evhtp_ssl_t * ssl) { +#ifndef EVHTP_DISABLE_SSL + X509 * cert; + BIO * bio; + unsigned char * raw_cert_str; + unsigned char * cert_str; + unsigned char * p; + size_t raw_cert_len; + size_t cert_len; + int i; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(bio = BIO_new(BIO_s_mem()))) { + X509_free(cert); + return NULL; + } + + if (!PEM_write_bio_X509(bio, cert)) { + BIO_free(bio); + X509_free(cert); + + return NULL; + } + + raw_cert_len = BIO_pending(bio); + raw_cert_str = calloc(raw_cert_len + 1, 1); + + BIO_read(bio, raw_cert_str, raw_cert_len); + + cert_len = raw_cert_len - 1; + + for (i = 0; i < raw_cert_len - 1; i++) { + if (raw_cert_str[i] == '\n') { + /* + * \n's will be converted to \r\n\t, so we must reserve + * enough space for that much data. + */ + cert_len += 2; + } + } + + /* 2 extra chars, one for possible last char (if not '\n'), and one for NULL terminator */ + cert_str = calloc(cert_len + 2, 1); + p = cert_str; + + for (i = 0; i < raw_cert_len - 1; i++) { + if (raw_cert_str[i] == '\n') { + *p++ = '\r'; + *p++ = '\n'; + *p++ = '\t'; + } else { + *p++ = raw_cert_str[i]; + } + } + + /* Don't assume last character is '\n' */ + if (raw_cert_str[i] != '\n') { + *p++ = raw_cert_str[i]; + } + + BIO_free(bio); + X509_free(cert); + free(raw_cert_str); + + return cert_str; +#else + return NULL; +#endif +} /* htp_sslutil_cert_tostr */ + +unsigned char * +htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { +#ifndef EVHTP_DISABLE_SSL + unsigned char * ext_str; + X509 * cert; + ASN1_OBJECT * oid_obj; + int oid_pos; + X509_EXTENSION * ext; + ASN1_OCTET_STRING * octet; + const unsigned char * octet_data; + long xlen; + int xtag; + int xclass; + + if (!ssl) { + return NULL; + } + + if (!(cert = SSL_get_peer_certificate(ssl))) { + return NULL; + } + + if (!(oid_obj = OBJ_txt2obj(oid, 1))) { + X509_free(cert); + return NULL; + } + + ext_str = NULL; + oid_pos = X509_get_ext_by_OBJ(cert, oid_obj, -1); + + if (!(ext = X509_get_ext(cert, oid_pos))) { + ASN1_OBJECT_free(oid_obj); + X509_free(cert); + return NULL; + } + + if (!(octet = X509_EXTENSION_get_data(ext))) { + ASN1_OBJECT_free(oid_obj); + X509_free(cert); + return NULL; + } + + octet_data = octet->data; + + if (ASN1_get_object(&octet_data, &xlen, &xtag, &xclass, octet->length)) { + ASN1_OBJECT_free(oid_obj); + X509_free(cert); + return NULL; + } + + /* We're only supporting string data. Could optionally add support + * for encoded binary data */ + + if (xlen > 0 && xtag == 0x0C && octet->type == V_ASN1_OCTET_STRING) { + ext_str = strndup(octet_data, xlen); + } + + ASN1_OBJECT_free(oid_obj); + X509_free(cert); + + return ext_str; +#else + return NULL; +#endif +} /* htp_sslutil_x509_ext_tostr */ From 607556571bfd96ecd851407ae228f37445fb4238 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Thu, 7 Dec 2017 07:19:30 +0100 Subject: [PATCH 02/19] Fix compilation with shared libraries `set_target_properties` is missing an argument name for setting SOVERSION, resulting in a build error when configuring: ``` CMake Error at CMakeLists.txt:199 (set_target_properties): set_target_properties called with incorrect number of arguments. ``` --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3617d97..74776b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ add_library (evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES}) target_link_libraries (evhtp ${LIBEVHTP_EXTERNAL_LIBS}) if (EVHTP_BUILD_SHARED) - set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" 0 OUTPUT_NAME "evhtp") + set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" SOVERSION 0 OUTPUT_NAME "evhtp") endif() add_subdirectory(examples) From d48e8bb4a0cbea9a58e9b05c4a0fd5cb1b573ade Mon Sep 17 00:00:00 2001 From: Nathan French Date: Thu, 7 Dec 2017 11:25:26 -0500 Subject: [PATCH 03/19] htp SSL utility API ufinalization (and examples) - added include/evhtp/sslutils.h for exported functions o only installs if SSL is enabled - only create https examples if SSL is enabled o this includes all the CA/Cert generation stuff too - updated example_https.c to display X headers for SSL connections using the new sslutils API - Updated examples/https/README.md - Removed need for EVHTP_DISABLE_SSL ifdefs in example_https.c as it will not be compiled unless enabled. --- CMakeLists.txt | 15 +++++- cmake/options.cmake | 8 ++-- examples/CMakeLists.txt | 28 +++++++----- examples/https/README.md | 50 ++++++++++++++++++++ examples/https/example_https.c | 83 +++++++++++++++++++++++++++++++++- include/evhtp/config.h.in | 1 + include/evhtp/sslutils.h | 30 ++++++++++++ sslutils.c | 37 +-------------- 8 files changed, 199 insertions(+), 53 deletions(-) create mode 100644 include/evhtp/sslutils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index fac0450..41ea6ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -185,8 +185,11 @@ set(LIBEVHTP_EXTERNAL_LIBS set (LIBEVHTP_SOURCE_FILES evhtp.c numtoa.c - parser.c - sslutils.c) + parser.c) + +if (NOT EVHTP_DISABLE_SSL) + list (APPEND LIBEVHTP_SOURCE_FILES sslutils.c) +endif() if (NOT EVHTP_DISABLE_EVTHR) list (APPEND LIBEVHTP_SOURCE_FILES thread.c) @@ -226,6 +229,14 @@ install ( DESTINATION ${INCLUDE_INSTALL_DIR}) +if (NOT EVHTP_DISABLE_SSL) + install ( + FILES + ${PROJECT_SOURCE_DIR}/include/evhtp/sslutils.h + DESTINATION + ${INCLUDE_INSTALL_DIR}/evhtp) +endif() + if (NOT EVHTP_DISABLE_EVTHR) install ( FILES diff --git a/cmake/options.cmake b/cmake/options.cmake index cee8f73..c874461 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -11,9 +11,11 @@ option (EVHTP_DISABLE_REGEX "Disable regex support" OFF) option (EVHTP_BUILD_SHARED "Build shared library too" OFF) # -DEVHTP_DEBUG:STRING=ON -option (EVHTP_DEBUG "Enable verbose debug logging" OFF) +option (EVHTP_DEBUG "Enable verbose debug logging" OFF) # can be overwritten by new set_alloc functions -option (EVHTP_USE_JEMALLOC "Enable jemalloc allocator" OFF) -option (EVHTP_USE_TCMALLOC "Enable tcmalloc allocator" OFF) +option (EVHTP_USE_JEMALLOC "Enable jemalloc allocator" OFF) +option (EVHTP_USE_TCMALLOC "Enable tcmalloc allocator" OFF) +# disable ability to wrap memory functions +option (EVHTP_DISABLE_MEMFUNCTIONS "Disable custom allocators" OFF) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 31c3b8b..ce8aec9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -8,7 +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) @@ -24,16 +24,22 @@ 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_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/) +if (NOT EVHTP_DISABLE_SSL) + 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) + + add_executable(example_https EXCLUDE_FROM_ALL https/example_https.c) + target_link_libraries(example_https evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) + + add_dependencies(examples example_https) +endif() -configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY) +add_dependencies(examples example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/https/README.md b/examples/https/README.md index 0e1b539..73a6a43 100644 --- a/examples/https/README.md +++ b/examples/https/README.md @@ -29,4 +29,54 @@ curl -kv \ --key examples/https/client1-key.pem \ --cert examples/https/client1-crt.pem \ https://localhost:4443/ + +``` + +The output (with client-certs) should look like: + +``` +< HTTP/1.1 200 OK +< X-SSL-Subject: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=client1/emailAddress=nate@cl0d.com +< X-SSL-Issuer: /C=US/ST=MA/L=Boston/O=Critical Stack/OU=evhtp/CN=ca/emailAddress=nate@cl0d.com +< X-SSL-Notbefore: Dec 7 16:10:34 2017 GMT +< X-SSL-Notafter: Sep 1 16:10:34 2020 GMT +< X-SSL-Serial: 57459A54BD08848C6D1546C2733EAD8A03553670 +< X-SSL-Cipher: ECDHE-RSA-AES256-GCM-SHA384 +< X-SSL-Sha1: 7A:68:47:CD:79:18:FF:DA:65:BC:67:6B:C2:5D:F3:66:9A:4A:64:7A +< X-SSL-Certificate: -----BEGIN CERTIFICATE----- +< MIIFkDCCA3igAwIBAgIUV0WaVL0IhIxtFUbCcz6tigNVNnAwDQYJKoZIhvcNAQEL +< BQAwfzELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1BMQ8wDQYDVQQHDAZCb3N0b24x +< FzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYDVQQLDAVldmh0cDELMAkGA1UE +< AwwCY2ExHDAaBgkqhkiG9w0BCQEWDW5hdGVAY2wwZC5jb20wHhcNMTcxMjA3MTYx +< MDM0WhcNMjAwOTAxMTYxMDM0WjCBhDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk1B +< MQ8wDQYDVQQHDAZCb3N0b24xFzAVBgNVBAoMDkNyaXRpY2FsIFN0YWNrMQ4wDAYD +< VQQLDAVldmh0cDEQMA4GA1UEAwwHY2xpZW50MTEcMBoGCSqGSIb3DQEJARYNbmF0 +< ZUBjbDBkLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVoTyUm +< 62PqJ9RHkNewV+0Dn6AvTVYXQRIejORB75e1OklAp3LGw+Nlc1iP5/MjKzTtMpxk +< kLDTDDhiX1mC2j9BDYOC6gpWEVosyU+fXaQvCxWKy4BASPUk7toLwgHxv855TTjV +< 2pe6VtAsImCT6sUGrKDnywAFvsBriXnzbTllm4gl7oPi8TrVZhk475JjEKgGKzsS +< wtpbxNUqiTXe5lQ/jU6oCCMWG1VWgPLLTIZElhp/TPqLO976DutPXuCBy56NoPMy +< DRm7YarhmG1vQNFdeJmC8/xdnKCbQpVhR9sF13tNIc+9QlTKNwzn375MK9e+xjXO +< nc8RuqRLeJcMrs5bx5Mtd28F+yNA0riGGtp72vse95bG0PoVUhAqzog4iceHtt6M +< 4jyyBhICHYKhkrAYoy3lQdfckiiu5GjZ4rPAq+PP4XPgcXYeqxph6OA+IJwUcWjK +< KjfQHvKJbIfo1ILQ4wzGxJ2KAE7F8CBrgokhYmshOMpDx6C4RPwifbtN2GcJN3Vc +< kaKGwE72PExFcCLKXwJvXTz+4P87JywCCtYXbUXlgn6rMe4JSRn1NZF00nkeV5bD +< AwCCoqSR5Qg9VUyhZKMF3zyQjHKS07SRDyl2vKLFUxnIu+6y4FxvnQqQOdDoz0BG +< Uf/s2KNRWK3w3i7hDz0mAQpXyeqmGilT4NZhAgMBAAEwDQYJKoZIhvcNAQELBQAD +< ggIBAIypf+0b+5xDRZ4IkcnlbUemZ0xt14UIw4N1Dr2kqp94gu4Z1nkLvYpLg61W +< sy3vJLDKc1kSPZG5sPEj/W9zophaSQzL8P/yLHQ3psk2+ie/XDmpmvMsvsVExgut +< lOExwMVfp+dIJ1cVfK5i8oMIE0IBbtdAIE/tzV+zzHpvAdA9KDcydW4oF+FLRLmQ +< +qfKnK1BkxWqQayNmsbVN63ao8i/4OKD5VtKGPC5RdsEURIDc579lFKACpUnQGaJ +< EKX/dKNiqoJSqOEDSsCN6jSJ7uTr5do+7xydqOcTQ+gI3FQsC1NjseqRRU0Q4HVL +< 95crEmqxlOxOjcrQK6U36HyKfn7EJ1B6/SJM8U9abOKBRUQjgLlrC6GaA/rToHmX +< mlkqw2nKTnZhvIGmi0UjwtOD8rQnGahnq+jwoQV6Ag2YbSfeygvajJvdLBjEBYm+ +< 5F6nQgv3JR9iJoS9AxcCEURH7jIAfdbYT6RBT3VARZyPcPtLSMFLLcXh0Z/Egifi +< f+xTIL7mCgdW2Jp5s8cNjhrWk6dJVaXwwA6MNSfDeWeu7uHRm3Ir0Jwoe5I2pENm +< mKueI6EhIKc6tdQWS6t+ZM0IJsVvhh4s0FqeUFYCP7RxG+P4u5wZxHdjbfUUJ8zA +< xHMrDvO8p6dwRUDAPkOqCPpdGmBky/ukBXNi2u0OOJ+wUgoA +< -----END CERTIFICATE----- +< Content-Length: 0 +< Content-Type: text/plain +< + ``` diff --git a/examples/https/example_https.c b/examples/https/example_https.c index 18a4723..e3b2a54 100644 --- a/examples/https/example_https.c +++ b/examples/https/example_https.c @@ -13,8 +13,88 @@ #include "evhtp/evhtp.h" #ifndef EVHTP_DISABLE_SSL +#include "evhtp/sslutils.h" + +static void +ssl__add_x_headers_(evhtp_headers_t * headers, evhtp_ssl_t * ssl) { + unsigned char * subj_str = NULL; + unsigned char * issr_str = NULL; + unsigned char * nbf_str = NULL; + unsigned char * naf_str = NULL; + unsigned char * ser_str = NULL; + unsigned char * cip_str = NULL; + unsigned char * sha1_str = NULL; + unsigned char * cert_str = NULL; + + if ((subj_str = htp_sslutil_subject_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Subject", subj_str, 0, 1)); + } + + if ((issr_str = htp_sslutil_issuer_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Issuer", issr_str, 0, 1)); + } + + if ((nbf_str = htp_sslutil_notbefore_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Notbefore", nbf_str, 0, 1)); + } + + if ((naf_str = htp_sslutil_notafter_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Notafter", naf_str, 0, 1)); + } + + if ((ser_str = htp_sslutil_serial_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Serial", ser_str, 0, 1)); + } + + if ((cip_str = htp_sslutil_cipher_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Cipher", cip_str, 0, 1)); + } + + if ((sha1_str = htp_sslutil_sha1_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Sha1", sha1_str, 0, 1)); + } + + if ((cert_str = htp_sslutil_cert_tostr(ssl))) { + evhtp_headers_add_header( + headers, + evhtp_header_new("X-SSL-Certificate", cert_str, 0, 1)); + } + + free(subj_str); + free(issr_str); + free(nbf_str); + free(naf_str); + free(ser_str); + free(cip_str); + free(sha1_str); + free(cert_str); +} /* ssl__add_x_headers_ */ + static void http__callback_(evhtp_request_t * req, void * arg) { + evhtp_connection_t * conn; + + evhtp_assert(req != NULL); + + conn = evhtp_request_get_connection(req); + evhtp_assert(conn != NULL); + + ssl__add_x_headers_(req->headers_out, conn->ssl); + return evhtp_send_reply(req, EVHTP_RES_OK); } @@ -135,7 +215,7 @@ parse__ssl_opts_(int argc, char ** argv) { ssl_config->ciphers = strdup(optarg); break; case OPTARG_VERIFY_DEPTH: - ssl_config->verify_depth = atoi(optarg); + ssl_config->verify_depth = atoi(optarg); break; case OPTARG_VERIFY_PEER: ssl_verify_mode |= SSL_VERIFY_PEER; @@ -220,6 +300,7 @@ parse__ssl_opts_(int argc, char ** argv) { return ssl_config; } /* parse__ssl_opts_ */ + #endif int diff --git a/include/evhtp/config.h.in b/include/evhtp/config.h.in index ae0280f..e5e434f 100644 --- a/include/evhtp/config.h.in +++ b/include/evhtp/config.h.in @@ -42,6 +42,7 @@ extern "C" { #cmakedefine EVHTP_USE_JEMALLOC #cmakedefine EVHTP_USE_TCMALLOC #cmakedefine EVHTP_DEBUG +#cmakedefine EVHTP_DISABLE_MEMFUNCTIONS #ifndef EVHTP_DISABLE_REGEX #include diff --git a/include/evhtp/sslutils.h b/include/evhtp/sslutils.h new file mode 100644 index 0000000..a20d81b --- /dev/null +++ b/include/evhtp/sslutils.h @@ -0,0 +1,30 @@ +/** + * @file sslutils.h + */ + +#ifndef __EVHTP_SSLUTILS_H__ +#define __EVHTP_SSLUTILS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +EVHTP_EXPORT unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl); +EVHTP_EXPORT unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/sslutils.c b/sslutils.c index 7078efb..a4a243c 100644 --- a/sslutils.c +++ b/sslutils.c @@ -7,11 +7,11 @@ #include "evhtp/config.h" #include "evhtp/evhtp.h" +#include "evhtp/sslutils.h" #include "internal.h" unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL unsigned char * subj_str; char * p; X509 * cert; @@ -41,14 +41,10 @@ htp_sslutil_subject_tostr(evhtp_ssl_t * ssl) { X509_free(cert); return subj_str; -#else - return NULL; -#endif } unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL X509 * cert; X509_NAME * name; char * p; @@ -78,14 +74,10 @@ htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl) { X509_free(cert); return issr_str; -#else - return NULL; -#endif } unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL BIO * bio; X509 * cert; ASN1_TIME * time; @@ -132,14 +124,10 @@ htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl) { X509_free(cert); return time_str; -#else - return NULL; -#endif } /* htp_sslutil_notbefore_tostr */ unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL BIO * bio; X509 * cert; ASN1_TIME * time; @@ -186,14 +174,10 @@ htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl) { X509_free(cert); return time_str; -#else - return NULL; -#endif } /* htp_sslutil_notafter_tostr */ unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL const EVP_MD * md_alg; X509 * cert; unsigned int n; @@ -242,14 +226,10 @@ htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl) { X509_free(cert); return buf; -#else - return NULL; -#endif } /* htp_sslutil_sha1_tostr */ unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL BIO * bio; X509 * cert; size_t len; @@ -286,14 +266,10 @@ htp_sslutil_serial_tostr(evhtp_ssl_t * ssl) { BIO_free(bio); return ser_str; -#else - return NULL; -#endif } /* htp_sslutil_serial_tostr */ unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL const SSL_CIPHER * cipher; const char * p; unsigned char * cipher_str; @@ -313,14 +289,10 @@ htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl) { cipher_str = strdup(p); return cipher_str; -#else - return NULL; -#endif } unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl) { -#ifndef EVHTP_DISABLE_SSL X509 * cert; BIO * bio; unsigned char * raw_cert_str; @@ -391,14 +363,10 @@ htp_sslutil_cert_tostr(evhtp_ssl_t * ssl) { free(raw_cert_str); return cert_str; -#else - return NULL; -#endif } /* htp_sslutil_cert_tostr */ unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { -#ifndef EVHTP_DISABLE_SSL unsigned char * ext_str; X509 * cert; ASN1_OBJECT * oid_obj; @@ -457,7 +425,4 @@ htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { X509_free(cert); return ext_str; -#else - return NULL; -#endif } /* htp_sslutil_x509_ext_tostr */ From 8f50b8354d53f5dc7be52d7e577d89655d384af5 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Sat, 9 Dec 2017 13:06:09 -0500 Subject: [PATCH 04/19] Extensive example of streaming data without hogging memory This is an example as a reply to https://github.com/criticalstack/libevhtp/issues/47 examples/example_chunked.c will open up a file (of any size), and stream data back to the client in 128 byte chunked segments. This is the proper way of sending large amounts of data back to a client without consuming much memory. --- examples/CMakeLists.txt | 4 +- examples/eutils.h | 10 +++ examples/example_chunked.c | 162 +++++++++++++++++++++++++++++++++++++ include/evhtp/evhtp.h | 9 +++ 4 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 examples/eutils.h create mode 100644 examples/example_chunked.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index ce8aec9..b0e8f56 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_chunked EXCLUDE_FROM_ALL example_chunked.c) if (NOT EVHTP_DISABLE_EVTHR) @@ -24,6 +25,7 @@ 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_chunked evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) if (NOT EVHTP_DISABLE_SSL) @@ -42,4 +44,4 @@ if (NOT EVHTP_DISABLE_SSL) add_dependencies(examples example_https) endif() -add_dependencies(examples example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) +add_dependencies(examples example_chunked example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/eutils.h b/examples/eutils.h new file mode 100644 index 0000000..051d1d0 --- /dev/null +++ b/examples/eutils.h @@ -0,0 +1,10 @@ +#pragma once + +static void * mm__dup_(const void * src, size_t size) { + void * mem = malloc(size); + + return mem ? memcpy(mem, src, size) : NULL; +} + +#define mm__alloc_(type, ...) \ + (type *)mm__dup_((type[]) {__VA_ARGS__ }, sizeof(type)) diff --git a/examples/example_chunked.c b/examples/example_chunked.c new file mode 100644 index 0000000..3a8f715 --- /dev/null +++ b/examples/example_chunked.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include "../log.h" +#include "./eutils.h" +#include "internal.h" +#include "evhtp/evhtp.h" + +struct reply_ { + evhtp_request_t * request; + FILE * file_desc; + struct evbuffer * buffer; +}; + + +/* This function is called each time the client has been sent + * all outstanding data. We use this to send the next part of + * the file in a chunk at 128 byte increments. + * + * When there is no more data to be read from the file, this + * will send the final chunked reply and free our struct reply_. + */ +static evhtp_res +http__send_chunk_(evhtp_connection_t * conn, void * arg) { + struct reply_ * reply = (struct reply_ *)arg; + char buf[128]; + size_t bytes_read; + + /* try to read 128 bytes from the file pointer */ + bytes_read = fread(buf, 1, sizeof(buf), reply->file_desc); + + log_info("Sending %zu bytes", bytes_read); + + if (bytes_read > 0) { + /* add our data we read from the file into our reply buffer */ + evbuffer_add(reply->buffer, buf, bytes_read); + + /* send the reply buffer as a http chunked message */ + evhtp_send_reply_chunk(reply->request, reply->buffer); + + /* we can now drain our reply buffer as to not be a resource + * hog. + */ + evbuffer_drain(reply->buffer, bytes_read); + } + + /* check if we have read everything from the file */ + if (feof(reply->file_desc)) { + log_info("Sending last chunk"); + + /* now that we have read everything from the file, we must + * first unset our on_write hook, then inform evhtp to send + * this message as the final chunk. + */ + evhtp_connection_unset_hook(conn, evhtp_hook_on_write); + evhtp_send_reply_chunk_end(reply->request); + + /* we can now free up our little reply_ structure */ + { + fclose(reply->file_desc); + evbuffer_free(reply->buffer); + free(reply); + } + } + + return EVHTP_RES_OK; +} + +/* This function is called when a request has been fully received. + * + * This function assumes the `arg` value is the filename that was + * passed via `evhtp_set_gencb` in `main`. + * + * 1. open the file + * 2. create a `struct reply_` + * 3. create an evbuffer that we will write into. + * 4. set a hook to call the function `http__send_chunk_` each + * time all data has been sent from the previous write call. + * 5. start the chunked stream via `evhtp_send_reply_chunk_start` + */ +static void +http__callback_(evhtp_request_t * req, void * arg) { + const char * filename = arg; + FILE * file_desc; + struct reply_ * reply; + + evhtp_assert(arg != NULL); + + /* open up the file as passed to us via evhtp_set_gencb */ + file_desc = fopen((const char *)arg, "r"); + evhtp_assert(file_desc != NULL); + + /* create our little internal reply structure which will + * be used by `http__send_chunk_` + */ + reply = mm__alloc_(struct reply_, { + req, + file_desc, + evbuffer_new() + }); + + /* here we set a connection hook of the type `evhtp_hook_on_write` + * + * this will execute the function `http__send_chunk_` each time + * all data has been written to the client. + */ + evhtp_connection_set_hook(req->conn, + evhtp_hook_on_write, + http__send_chunk_, reply); + + /* we do not have to start sending data from the file from here - + * this function will write data to the client, thus when finished, + * will call our `http__send_chunk_` callback. + */ + evhtp_send_reply_chunk_start(req, EVHTP_RES_OK); +} + +int +main(int argc, char ** argv) { + evhtp_t * htp; + struct event_base * evbase; + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + exit(EXIT_FAILURE); + } + + evbase = event_base_new(); + evhtp_alloc_assert(evbase); + + htp = evhtp_new(evbase, NULL); + evhtp_alloc_assert(htp); + + /* here we set our default request response callback, the argument + * that is passed will be the filename we want to stream to the + * client in chunked form. + */ + evhtp_set_gencb(htp, http__callback_, strdup(argv[1])); + + 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("curl http://127.0.0.1:%d/", port); + } + + event_base_loop(evbase, 0); + + + return 0; +} diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index f7cd3d9..f53478a 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -775,6 +775,10 @@ EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_ty 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); +EVHTP_EXPORT evhtp_hooks_t * evhtp_connection_get_hooks(evhtp_connection_t * c); +EVHTP_EXPORT evhtp_hooks_t * evhtp_request_get_hooks(evhtp_request_t * r); +EVHTP_EXPORT evhtp_hooks_t * evhtp_callback_get_hooks(evhtp_callback_t * cb); + /** * @brief removes all hooks. * @@ -784,6 +788,11 @@ EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type */ EVHTP_EXPORT int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); +EVHTP_EXPORT int evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type); +EVHTP_EXPORT int evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type); +EVHTP_EXPORT int evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type); + + /** * @brief bind to a socket, optionally with specific protocol support * formatting. The addr can be defined as one of the following: From d8908997a28c0900138f48fa34be050124cb7828 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Sun, 10 Dec 2017 21:57:21 -0500 Subject: [PATCH 05/19] added some new functions to examples/eutils --- examples/eutils.h | 15 +++++++++++++++ examples/example_chunked.c | 15 +-------------- examples/example_pause.c | 22 ++++------------------ examples/example_vhost.c | 16 +++------------- 4 files changed, 23 insertions(+), 45 deletions(-) diff --git a/examples/eutils.h b/examples/eutils.h index 051d1d0..1da4dd7 100644 --- a/examples/eutils.h +++ b/examples/eutils.h @@ -8,3 +8,18 @@ static void * mm__dup_(const void * src, size_t size) { #define mm__alloc_(type, ...) \ (type *)mm__dup_((type[]) {__VA_ARGS__ }, sizeof(type)) + +#define bind__sock_port0_(HTP) ({ \ + struct sockaddr_in sin; \ + socklen_t len = len = sizeof(struct sockaddr); \ + uint16_t port; \ + \ + evhtp_bind_socket(HTP, "127.0.0.1", 0, 128); \ + \ + getsockname( \ + evconnlistener_get_fd(HTP->server), \ + (struct sockaddr *)&sin, &len); \ + \ + port = ntohs(sin.sin_port); \ + port; \ + }) diff --git a/examples/example_chunked.c b/examples/example_chunked.c index 3a8f715..060c6f1 100644 --- a/examples/example_chunked.c +++ b/examples/example_chunked.c @@ -140,20 +140,7 @@ main(int argc, char ** argv) { */ evhtp_set_gencb(htp, http__callback_, strdup(argv[1])); - 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("curl http://127.0.0.1:%d/", port); - } + log_info("curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); event_base_loop(evbase, 0); diff --git a/examples/example_pause.c b/examples/example_pause.c index 247a7da..9378afa 100644 --- a/examples/example_pause.c +++ b/examples/example_pause.c @@ -14,6 +14,7 @@ #include "../log.h" #include "internal.h" #include "evhtp/evhtp.h" +#include "./eutils.h" struct paused_request_ { struct event * _timeoutev; @@ -103,23 +104,8 @@ main(int argc, char ** argv) { */ 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; + log_info("response delayed for 10s: " + "curl http://127.0.0.1:%d/", bind__sock_port0_(htp)); - 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; + return event_base_loop(evbase, 0); } diff --git a/examples/example_vhost.c b/examples/example_vhost.c index 4a61003..9116b21 100644 --- a/examples/example_vhost.c +++ b/examples/example_vhost.c @@ -7,6 +7,7 @@ #include "internal.h" #include "../log.h" #include "evhtp/evhtp.h" +#include "./eutils.h" #define make_response(cb) do { \ evbuffer_add_printf(req->buffer_out, \ @@ -94,19 +95,8 @@ main(int argc, char ** argv) { /* 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); + uint16_t port = bind__sock_port0_(htp); log_info("[[ try the following commands and you should see 'evhtp.io domains' ]]"); log_info("====================================================================="); @@ -123,5 +113,5 @@ main(int argc, char ** argv) { log_info("curl -H'Host: gmail.google.com' http://127.0.0.1:%d/vhost", port); } - event_base_loop(evbase, 0); + return event_base_loop(evbase, 0); } /* main */ From 756a7e2e8304a1bd802ffc7fd787ec11d9e9c21f Mon Sep 17 00:00:00 2001 From: Nathan French Date: Thu, 14 Dec 2017 04:53:48 -0500 Subject: [PATCH 06/19] Added EVHTP_FLAG_ENABLE_ALL to enable all socket flags --- include/evhtp/evhtp.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h index f53478a..383aebd 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -298,6 +298,11 @@ struct evhtp_s { #define EVHTP_FLAG_ENABLE_NODELAY (1 << 3) #define EVHTP_FLAG_ENABLE_DEFER_ACCEPT (1 << 4) #define EVHTP_FLAG_DEFAULTS EVHTP_FLAG_ENABLE_100_CONT + #define EVHTP_FLAG_ENABLE_ALL EVHTP_FLAG_ENABLE_100_CONT \ + | EVHTP_FLAG_ENABLE_REUSEPORT \ + | EVHTP_FLAG_ENABLE_NODELAY \ + | EVHTP_FLAG_ENABLE_DEFER_ACCEPT + uint16_t flags; /**< the base flags set for this context, see: EVHTP_FLAG_* */ uint16_t parser_flags; /**< default query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) */ From e5a3bdfbef383af049765c8d0a1dd904aabcfc44 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Thu, 14 Dec 2017 09:03:18 -0500 Subject: [PATCH 07/19] only warn if setsockopt fails on EOPNOTSUPP --- evhtp.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/evhtp.c b/evhtp.c index d9e1bb1..53255a7 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3964,7 +3964,11 @@ evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog) { if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on)) == -1) { - break; + if (errno != EOPNOTSUPP) { + break; + } + + log_warn("SO_REUSEPORT not supported for this socket.. Skipping"); } } #endif @@ -3974,7 +3978,11 @@ evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog) { if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) == -1) { - break; + if (errno != EOPNOTSUPP) { + break; + } + + log_warn("TCP_NODELAY not supported for this socket.. Skipping"); } } #endif @@ -3984,7 +3992,11 @@ evhtp_accept_socket(evhtp_t * htp, evutil_socket_t sock, int backlog) { if (setsockopt(sock, IPPROTO_TCP, TCP_DEFER_ACCEPT, (void *)&on, sizeof(on)) == -1) { - break; + if (errno != EOPNOTSUPP) { + break; + } + + log_warn("TCP_DEFER_ACCEPT not supported for this socket.. Skipping"); } } #endif @@ -5176,8 +5188,8 @@ evhtp_add_alias(evhtp_t * evhtp, const char * name) int evhtp_add_aliases(evhtp_t * htp, const char * name, ...) { - va_list argp; - size_t len; + va_list argp; + size_t len; if (evhtp_add_alias(htp, name) == -1) { return -1; From 22207ad9192ddfc5bffec341925b8050ccc4b51a Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 11:10:54 -0500 Subject: [PATCH 08/19] sslutil API updates [documentation / parsers / x-hdr helpers] - htp_sslutil_verify2opts() : helper function to convert a string to the proper SSL_VERIFY_* flags (on / off / optional) - htp_sslutil_add_xheaders() : helper function to populate a headers context with X-SSL type headers: the last argument `flags` is an OR'd mask of what X-Header to include: * HTP_SSLUTILS_XHDR_SUBJ: `X-SSL-Subject` * HTP_SSLUTILS_XHDR_ISSR: `X-SSL-Issuer` * HTP_SSLUTILS_XHDR_NBFR: `X-SSL-Notbefore` * HTP_SSLUTILS_XHDR_NAFR: `X-SSL-Notafter` * HTP_SSLUTILS_XHDR_SERL: `X-SSL-Serial` * HTP_SSLUTILS_XHDR_CIPH: `X-SSL-Cipher` * HTP_SSLUTILS_XHDR_CERT: `X-SSL-Certificate` * HTP_SSLUTILS_XHDR_SHA1: `X-SSL-SHA1` - Added doxygen documentation for all ssutil functions - Renamed example_https.c to example_https_server.c --- examples/CMakeLists.txt | 6 +- ...example_https.c => example_https_server.c} | 74 +-------- include/evhtp/sslutils.h | 150 ++++++++++++++++++ sslutils.c | 76 +++++++++ 4 files changed, 233 insertions(+), 73 deletions(-) rename examples/https/{example_https.c => example_https_server.c} (82%) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index b0e8f56..1479d91 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -38,10 +38,10 @@ if (NOT EVHTP_DISABLE_SSL) configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY) - add_executable(example_https EXCLUDE_FROM_ALL https/example_https.c) - target_link_libraries(example_https evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) + add_executable(example_https_server EXCLUDE_FROM_ALL https/example_https_server.c) + target_link_libraries(example_https_server evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) - add_dependencies(examples example_https) + add_dependencies(examples example_https_server) endif() add_dependencies(examples example_chunked example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/https/example_https.c b/examples/https/example_https_server.c similarity index 82% rename from examples/https/example_https.c rename to examples/https/example_https_server.c index e3b2a54..586f2d4 100644 --- a/examples/https/example_https.c +++ b/examples/https/example_https_server.c @@ -15,75 +15,6 @@ #ifndef EVHTP_DISABLE_SSL #include "evhtp/sslutils.h" -static void -ssl__add_x_headers_(evhtp_headers_t * headers, evhtp_ssl_t * ssl) { - unsigned char * subj_str = NULL; - unsigned char * issr_str = NULL; - unsigned char * nbf_str = NULL; - unsigned char * naf_str = NULL; - unsigned char * ser_str = NULL; - unsigned char * cip_str = NULL; - unsigned char * sha1_str = NULL; - unsigned char * cert_str = NULL; - - if ((subj_str = htp_sslutil_subject_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Subject", subj_str, 0, 1)); - } - - if ((issr_str = htp_sslutil_issuer_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Issuer", issr_str, 0, 1)); - } - - if ((nbf_str = htp_sslutil_notbefore_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Notbefore", nbf_str, 0, 1)); - } - - if ((naf_str = htp_sslutil_notafter_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Notafter", naf_str, 0, 1)); - } - - if ((ser_str = htp_sslutil_serial_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Serial", ser_str, 0, 1)); - } - - if ((cip_str = htp_sslutil_cipher_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Cipher", cip_str, 0, 1)); - } - - if ((sha1_str = htp_sslutil_sha1_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Sha1", sha1_str, 0, 1)); - } - - if ((cert_str = htp_sslutil_cert_tostr(ssl))) { - evhtp_headers_add_header( - headers, - evhtp_header_new("X-SSL-Certificate", cert_str, 0, 1)); - } - - free(subj_str); - free(issr_str); - free(nbf_str); - free(naf_str); - free(ser_str); - free(cip_str); - free(sha1_str); - free(cert_str); -} /* ssl__add_x_headers_ */ - static void http__callback_(evhtp_request_t * req, void * arg) { evhtp_connection_t * conn; @@ -93,7 +24,10 @@ http__callback_(evhtp_request_t * req, void * arg) { conn = evhtp_request_get_connection(req); evhtp_assert(conn != NULL); - ssl__add_x_headers_(req->headers_out, conn->ssl); + htp_sslutil_add_xheaders( + req->headers_out, + conn->ssl, + HTP_SSLUTILS_XHDR_ALL); return evhtp_send_reply(req, EVHTP_RES_OK); } diff --git a/include/evhtp/sslutils.h b/include/evhtp/sslutils.h index a20d81b..68daa8c 100644 --- a/include/evhtp/sslutils.h +++ b/include/evhtp/sslutils.h @@ -11,17 +11,167 @@ extern "C" { #endif +/** + * @defgroup htp_sslutils SSL utility functions + */ + +/** + * @brief converts the client certificate DNAME information (CN=, OU=.....) + * @ingroup htp_sslutils + * + * @param ssl the client SSL context + * + * @return heap allocated str representation, NULL on error. + */ EVHTP_EXPORT unsigned char * htp_sslutil_subject_tostr(evhtp_ssl_t * ssl); + + +/** + * @brief converts the DN (issuer of cert from the client) + * @ingroup htp_sslutils + * + * @param ssl client SSL context + * + * @return heap allocated str representation, NULL on error + */ EVHTP_EXPORT unsigned char * htp_sslutil_issuer_tostr(evhtp_ssl_t * ssl); + + +/** + * @brief converts the `notbefore` date of the cert from the client + * @ingroup htp_sslutils + * + * @param ssl client SSL context + * + * @return heap allocated str (YYMMDDhhmmss) of the notbefore, NULL on error. + */ EVHTP_EXPORT unsigned char * htp_sslutil_notbefore_tostr(evhtp_ssl_t * ssl); + + +/** + * @brief converts the `notafter` date of the cert from the client + * @ingroup htp_sslutils + * + * @param ssl ssl client SSL context + * + * @return heap allocated str (YYMMDDhhmmss) of notafter, NULL on error. + */ EVHTP_EXPORT unsigned char * htp_sslutil_notafter_tostr(evhtp_ssl_t * ssl); + + +/** + * @brief converts the SHA1 digest in str from the client + * @ingroup htp_sslutils + * + * @param ssl SSL context from client + * + * @return NULL on error + */ EVHTP_EXPORT unsigned char * htp_sslutil_sha1_tostr(evhtp_ssl_t * ssl); + +/** + * @brief convert serial number to string + * @ingroup htp_sslutils + * + * @param ssl SSL context from client + * + * @return NULL on error + */ EVHTP_EXPORT unsigned char * htp_sslutil_serial_tostr(evhtp_ssl_t * ssl); + +/** + * @brief convert the used for this SSL context + * @ingroup htp_sslutils + * + * @param ssl SSL context + * + * @return heap allocated cipher str, NULL on error + */ EVHTP_EXPORT unsigned char * htp_sslutil_cipher_tostr(evhtp_ssl_t * ssl); + +/** + * @brief convert the client cert into a multiline string + * @ingroup htp_sslutils + * + * @param ssl client SSL context + * + * @return heap allocated string, NULL on error + */ EVHTP_EXPORT unsigned char * htp_sslutil_cert_tostr(evhtp_ssl_t * ssl); + + +/** + * @brief convert X509 extentions to string + * @ingroup htp_sslutils + * + * @param ssl SSL context + * @param oid + * + * @return + */ EVHTP_EXPORT unsigned char * htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid); +/** + * @brief convert a string to the proper verify opts + * @ingroup htp_sslutils + * + * @param opts_str ("on" / "optional" / "off" ) + * where: + * "on" : client must present a valid cert (otherwise rejected) + * "off" : no client cert required at all + * "optional" : client MAY present a valid certificate (but not rejected) + * + * @note if `opts_str` is NULL, defaults to "off" + * + * @return OR'd mask SSL_VERIFY_* flags, -1 on error + */ +EVHTP_EXPORT int htp_sslutil_verify2opts(const char * opts_str); + +/* + * @ingroup htp_sslutils + * @ { + */ +#define HTP_SSLUTILS_XHDR_SUBJ (1 << 0) +#define HTP_SSLUTILS_XHDR_ISSR (1 << 1) +#define HTP_SSLUTILS_XHDR_NBFR (1 << 2) +#define HTP_SSLUTILS_XHDR_NAFR (1 << 3) +#define HTP_SSLUTILS_XHDR_SERL (1 << 4) +#define HTP_SSLUTILS_XHDR_SHA1 (1 << 5) +#define HTP_SSLUTILS_XHDR_CERT (1 << 6) +#define HTP_SSLUTILS_XHDR_CIPH (1 << 7) +#define HTP_SSLUTILS_XHDR_ALL \ + HTP_SSLUTILS_XHDR_SUBJ \ + | HTP_SSLUTILS_XHDR_ISSR \ + | HTP_SSLUTILS_XHDR_NBFR \ + | HTP_SSLUTILS_XHDR_NAFR \ + | HTP_SSLUTILS_XHDR_SERL \ + | HTP_SSLUTILS_XHDR_SHA1 \ + | HTP_SSLUTILS_XHDR_CERT \ + | HTP_SSLUTILS_XHDR_CIPH +/** @} */ + +/** + * @brief add SSL-type X-Header flags to an evhtp_headers_t context + * @ingroup htp_sslutils + * + * @param hdrs headers structure to append into + * @param ssl the SSL context + * HTP_SSLUTILS_XHDR_SUBJ: `X-SSL-Subject` + * HTP_SSLUTILS_XHDR_ISSR: `X-SSL-Issuer` + * HTP_SSLUTILS_XHDR_NBFR: `X-SSL-Notbefore` + * HTP_SSLUTILS_XHDR_NAFR: `X-SSL-Notafter` + * HTP_SSLUTILS_XHDR_SERL: `X-SSL-Serial` + * HTP_SSLUTILS_XHDR_CIPH: `X-SSL-Cipher` + * HTP_SSLUTILS_XHDR_CERT: `X-SSL-Certificate` + * HTP_SSLUTILS_XHDR_SHA1: `X-SSL-SHA1` + * + * @param flags flags (See XHDR defines above) + * + * @return 0 on success, -1 on error + */ +EVHTP_EXPORT int htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags); + #ifdef __cplusplus } #endif diff --git a/sslutils.c b/sslutils.c index a4a243c..6aabf21 100644 --- a/sslutils.c +++ b/sslutils.c @@ -426,3 +426,79 @@ htp_sslutil_x509_ext_tostr(evhtp_ssl_t * ssl, const char * oid) { return ext_str; } /* htp_sslutil_x509_ext_tostr */ + +int +htp_sslutil_verify2opts(const char * opts_str) { + if (!opts_str || !strcasecmp(opts_str, "off")) { + return SSL_VERIFY_NONE; + } + + if (!strcasecmp(opts_str, "optional")) { + return SSL_VERIFY_PEER; + } + + if (!strcasecmp(opts_str, "on")) { + return SSL_VERIFY_PEER + | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + } + + return -1; +} + +int +htp_sslutil_add_xheaders(evhtp_headers_t * hdrs, evhtp_ssl_t * ssl, short flags) { + int i; + + if (!hdrs || !ssl) { + return -1; + } + + struct { + const char * hdr_str; + short flag; + unsigned char * (* convfn)(evhtp_ssl_t *); + } ssl_x_hdrs_[] = { + { "X-SSL-Subject", HTP_SSLUTILS_XHDR_SUBJ, htp_sslutil_subject_tostr }, + { "X-SSL-Issuer", HTP_SSLUTILS_XHDR_ISSR, htp_sslutil_issuer_tostr }, + { "X-SSL-Notbefore", HTP_SSLUTILS_XHDR_NBFR, htp_sslutil_notbefore_tostr }, + { "X-SSL-Notafter", HTP_SSLUTILS_XHDR_NAFR, htp_sslutil_notafter_tostr }, + { "X-SSL-Serial", HTP_SSLUTILS_XHDR_SERL, htp_sslutil_serial_tostr }, + { "X-SSL-Cipher", HTP_SSLUTILS_XHDR_CIPH, htp_sslutil_cipher_tostr }, + { "X-SSL-Certificate", HTP_SSLUTILS_XHDR_CERT, htp_sslutil_cert_tostr }, + { "X-SSL-SHA1", HTP_SSLUTILS_XHDR_SHA1, htp_sslutil_sha1_tostr }, + { NULL, 0, NULL } + }; + + /* remove all of the current x- headers if present */ + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Subject")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Issuer")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notbefore")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Notafter")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Serial")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Cipher")); + evhtp_kv_rm_and_free(hdrs, evhtp_kvs_find_kv(hdrs, "X-SSL-Certificate")); + + if (flags == 0) { + return 0; + } + + /* iterate over our ssl_x_hdrs_ struct array, compare the flags, + * and if a xhdr flag is set, run the proper *_tostr function and + * append it to the `hdrs` passed to this function. + */ + for (i = 0; ssl_x_hdrs_[i].hdr_str; i++) { + char * o_str = NULL; + + if (flags & ssl_x_hdrs_[i].flag) { + if ((o_str = (ssl_x_hdrs_[i].convfn)(ssl))) { + evhtp_headers_add_header( + hdrs, + evhtp_header_new(ssl_x_hdrs_[i].hdr_str, o_str, 0, 1)); + + evhtp_safe_free(o_str, free); + } + } + } + + return 0; +} /* htp_sslutil_add_xheaders */ From e32de9e92cf043a6ed900e2f69a7dc97696b645f Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 11:26:38 -0500 Subject: [PATCH 09/19] Update example/https/README for name changes --- examples/https/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/https/README.md b/examples/https/README.md index 73a6a43..075cf17 100644 --- a/examples/https/README.md +++ b/examples/https/README.md @@ -7,7 +7,7 @@ After running `make examples`, if SSL is enabled, you can quickly test HTTPS, wi # Test without client auth # Run the server -./examples/example_https \ +./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem @@ -16,7 +16,7 @@ curl -vk https://localhost:4443/ # Test WITH client auth -./examples/example_https \ +./examples/example_https_server \ -cert examples/https/server-crt.pem \ -key examples/https/server-key.pem \ -ca examples/https/ca-crt.pem \ From 381907388818ba40455cfdfbd7c1425edbff514a Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 14:05:54 -0500 Subject: [PATCH 10/19] OpenSSL 1.1.0 updates (load_*/_init/_add*) --- evhtp.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 53255a7..a6736b7 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4779,11 +4779,28 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) return -1; } +#if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); ERR_load_crypto_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); - +#else + /* unnecessary in OpenSSL 1.1.0 */ + /* + * if (OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL) == 0) { + * log_error("OPENSSL_init_ssl"); + * return -1; + * } + * + * if (OPENSSL_init_crypto( + * OPENSSL_INIT_ADD_ALL_CIPHERS | + * OPENSSL_INIT_ADD_ALL_DIGESTS | + * OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { + * log_error("OPENSSL_init_crypto"); + * return -1; + * } + */ +#endif if (RAND_poll() != 1) { log_error("RAND_poll"); return -1; @@ -4800,7 +4817,11 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) #endif htp->ssl_cfg = cfg; +#if OPENSSL_VERSION_NUMBER < 0x10100000L htp->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); +#else + htp->ssl_ctx = SSL_CTX_new(TLS_server_method()); +#endif evhtp_alloc_assert(htp->ssl_ctx); From 1842f6f6b7cc11ecc46b807965bf1084de0282da Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 17:27:39 -0500 Subject: [PATCH 11/19] log format updates --- log.h | 67 ++++++++++++++++++++++++++++++++------------------------ parser.c | 42 +++++++++++++++++------------------ 2 files changed, 58 insertions(+), 51 deletions(-) diff --git a/log.h b/log.h index e8b7ff6..66cf8fd 100644 --- a/log.h +++ b/log.h @@ -11,39 +11,48 @@ #define clean_errno() \ (errno == 0 ? "None" : strerror(errno)) +#define __log_debug_color(X) "[\x1b[1;36m" X "\x1b[0;39m]" +#define __log_error_color(X) "[\x1b[1;31m" X "\x1b[0;39m]" +#define __log_warn_color(X) "[\x1b[1;33m" X "\x1b[0;39m]" +#define __log_info_color(X) "[\x1b[32m" X "\x1b[0;39m]" +#define __log_func_color(X) "\x1b[33m" X "\x1b[39m" +#define __log_args_color(X) "\x1b[94m" X "\x1b[39m" +#define __log_errno_color(X) "\x1b[35m" X "\x1b[39m" + #if !defined(EVHTP_DEBUG) /* compile with all debug messages removed */ #define log_debug(M, ...) #else -#define log_debug(M, ...) \ - fprintf(stderr, "\33[34mDEBUG\33[39m "M " \33[90m at %s (%s:%d) \33[39m\n", ## \ - __VA_ARGS__, \ - __func__, \ - __FILE__, \ - __LINE__) +#define log_debug(M, ...) \ + fprintf(stderr, __log_debug_color("DEBUG") " " \ + __log_func_color("%s:%-9d") \ + __log_args_color(M) \ + "\n", \ + __FILENAME__, __LINE__, ## __VA_ARGS__) #endif - -#define log_error(M, ...) \ - fprintf(stderr, "\33[31mERR\33[39m " M " \33[90m at %s (%s:%d) \33[94merrno: %s\33[39m\n", ## \ - __VA_ARGS__, \ - __func__, \ - __FILE__, \ - __LINE__, \ - clean_errno()) - -#define log_warn(M, ...) \ - fprintf(stderr, "\33[91mWARN\33[39m " M " \33[90m at %s (%s:%d) \33[94merrno: %s\33[39m\n", ## \ - __VA_ARGS__, \ - __func__, \ - __FILE__, \ - __LINE__, \ - clean_errno()) - -#define log_info(M, ...) \ - fprintf(stderr, "\33[32mINFO\33[39m " M " \33[90m at %s (%s:%d) \33[39m\n", ## \ - __VA_ARGS__, \ - __func__, \ - __FILENAME__, \ - __LINE__) +#define log_error(M, ...) \ + fprintf(stderr, __log_error_color("ERROR") " " \ + __log_func_color("%s:%-9d") \ + __log_args_color(M) \ + " :: " \ + __log_errno_color("(errno: %s)") \ + "\n", \ + __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) + + +#define log_warn(M, ...) \ + fprintf(stderr, __log_warn_color("WARN") " " \ + __log_func_color("%s:%-9d") \ + __log_args_color(M) \ + " :: " \ + __log_errno_color("(errno: %s)") \ + "\n", \ + __FILENAME__, __LINE__, ## __VA_ARGS__, clean_errno()) + +#define log_info(M, ...) \ + fprintf(stderr, __log_info_color("INFO") " " \ + __log_func_color("%4s:%-9d") \ + __log_args_color(M) "\n", \ + __FILENAME__, __LINE__, ## __VA_ARGS__) diff --git a/parser.c b/parser.c index cda246f..5ab418e 100644 --- a/parser.c +++ b/parser.c @@ -300,30 +300,28 @@ static const char * method_strmap[] = { #endif -#define __HTPARSE_GENHOOK(__n) \ - static inline int hook_ ## __n ## _run(htparser * p, htparse_hooks * hooks) \ - { \ - log_debug("enter"); \ - if (hooks && (hooks)->__n) \ - { \ - return (hooks)->__n(p); \ - } \ - \ - return 0; \ +#define __HTPARSE_GENHOOK(__n) \ + static inline int hook_ ## __n ## _run(htparser * p, htparse_hooks * hooks) { \ + log_debug("enter"); \ + if (hooks && (hooks)->__n) \ + { \ + return (hooks)->__n(p); \ + } \ + \ + return 0; \ } -#define __HTPARSE_GENDHOOK(__n) \ - static inline int hook_ ## __n ## _run(htparser * p, \ - htparse_hooks * hooks, \ - const char * s, size_t l) \ - { \ - log_debug("enter"); \ - if (hooks && (hooks)->__n) \ - { \ - return (hooks)->__n(p, s, l); \ - } \ - \ - return 0; \ +#define __HTPARSE_GENDHOOK(__n) \ + static inline int hook_ ## __n ## _run(htparser * p, \ + htparse_hooks * hooks, \ + const char * s, size_t l) { \ + log_debug("enter"); \ + if (hooks && (hooks)->__n) \ + { \ + return (hooks)->__n(p, s, l); \ + } \ + \ + return 0; \ } __HTPARSE_GENHOOK(on_msg_begin) From 086afd1f490f1184dfb66e20a48fb24e9c59ee89 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 17:36:25 -0500 Subject: [PATCH 12/19] example_https_server now uses htp_sslutil_verify2opts --- examples/https/README.md | 9 +++--- examples/https/example_https_server.c | 44 +++++++++++++-------------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/examples/https/README.md b/examples/https/README.md index 075cf17..ea95044 100644 --- a/examples/https/README.md +++ b/examples/https/README.md @@ -9,7 +9,8 @@ After running `make examples`, if SSL is enabled, you can quickly test HTTPS, wi # Run the server ./examples/example_https_server \ -cert examples/https/server-crt.pem \ - -key examples/https/server-key.pem + -key examples/https/server-key.pem \ + -verify-client off # Make a request curl -vk https://localhost:4443/ @@ -20,16 +21,14 @@ curl -vk https://localhost:4443/ -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 + -verify-client on \ + -verify-depth 2 # Make a request with the client key curl -kv \ --key examples/https/client1-key.pem \ --cert examples/https/client1-crt.pem \ https://localhost:4443/ - ``` The output (with client-certs) should look like: diff --git a/examples/https/example_https_server.c b/examples/https/example_https_server.c index 586f2d4..9dbe795 100644 --- a/examples/https/example_https_server.c +++ b/examples/https/example_https_server.c @@ -74,7 +74,6 @@ enum { OPTARG_CAPATH, OPTARG_CIPHERS, OPTARG_VERIFY_PEER, - OPTARG_ENFORCE_PEER_CERT, OPTARG_VERIFY_DEPTH, OPTARG_ENABLE_CACHE, OPTARG_CACHE_TIMEOUT, @@ -91,8 +90,11 @@ static const char * help = " -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-client (on | off | optional)\n" + " Enables verification of client certificates. \n" + " on : the client has to present a valid cert \n" + " off : no client cert is required at all \n" + " optional : the client may present a valid 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" @@ -110,22 +112,21 @@ parse__ssl_opts_(int argc, char ** argv) { 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 } + { "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-client", required_argument, 0, OPTARG_VERIFY_PEER }, + { "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) { @@ -152,10 +153,7 @@ parse__ssl_opts_(int argc, char ** argv) { 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; + ssl_verify_mode = htp_sslutil_verify2opts(optarg); break; case OPTARG_ENABLE_CACHE: ssl_config->scache_type = evhtp_ssl_scache_type_internal; From 933febfc8151fa92f3cbda78c4a26c9c975a7e72 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 18 Dec 2017 19:25:21 -0500 Subject: [PATCH 13/19] added example_https_client.c --- examples/CMakeLists.txt | 5 +- examples/https/example_https_client.c | 170 ++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 examples/https/example_https_client.c diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 1479d91..4f5cdb2 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -41,7 +41,10 @@ if (NOT EVHTP_DISABLE_SSL) add_executable(example_https_server EXCLUDE_FROM_ALL https/example_https_server.c) target_link_libraries(example_https_server evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) - add_dependencies(examples example_https_server) + add_executable(example_https_client EXCLUDE_FROM_ALL https/example_https_client.c) + target_link_libraries(example_https_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) + + add_dependencies(examples example_https_server example_https_client) endif() add_dependencies(examples example_chunked example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf) diff --git a/examples/https/example_https_client.c b/examples/https/example_https_client.c new file mode 100644 index 0000000..964c3eb --- /dev/null +++ b/examples/https/example_https_client.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../log.h" +#include "internal.h" +#include "evhtp/evhtp.h" + +#ifndef EVHTP_DISABLE_SSL +#include "evhtp/sslutils.h" + +static int +print_header_(evhtp_header_t * header, void * arg) { + fprintf(stderr, "%s: %s\n", header->key, header->val); + return 0; +} + +static void +https_resp_(evhtp_request_t * req, void * arg) { + evhtp_headers_for_each(req->headers_in, print_header_, NULL); + + if (evbuffer_get_length(req->buffer_in)) { + fprintf(stderr, "got: %.*s\n", + (int)evbuffer_get_length(req->buffer_in), + evbuffer_pullup(req->buffer_in, -1)); + } + + /* since we only made one request, we break the event loop */ + event_base_loopbreak((struct event_base *)arg); +} + +enum { + OPTARG_CERT = 1000, + OPTARG_KEY, + OPTARG_ADDR, + OPTARG_PORT, + OPTARG_SNI +}; +#endif + +int +main(int argc, char ** argv) { +#ifndef EVHTP_DISABLE_SSL + struct event_base * evbase; + evhtp_connection_t * conn; + evhtp_request_t * req; + evhtp_ssl_ctx_t * ctx; + char * addr = NULL; + uint16_t port = 4443; + char * key = NULL; + char * crt = NULL; + int opt = 0; + int long_index = 0; + int res; + + static struct option long_options[] = { + { "cert", required_argument, 0, OPTARG_CERT }, + { "key", required_argument, 0, OPTARG_KEY }, + { "addr", required_argument, 0, OPTARG_ADDR }, + { "port", required_argument, 0, OPTARG_PORT }, + { "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("Usage: %s\n" + " -key \n" + " -cert \n" + " -addr \n" + " -port \n", argv[0]); + return 0; + case OPTARG_CERT: + crt = strdup(optarg); + break; + case OPTARG_KEY: + key = strdup(optarg); + break; + case OPTARG_ADDR: + addr = strdup(optarg); + break; + case OPTARG_PORT: + port = atoi(optarg); + break; + } /* switch */ + } + + + evbase = event_base_new(); + evhtp_alloc_assert(evbase); + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + ctx = SSL_CTX_new(SSLv23_client_method()); +#else + ctx = SSL_CTX_new(TLS_client_method()); +#endif + evhtp_assert(ctx != NULL); + + if (key) { + /* client private key file defined, so use it */ + res = SSL_CTX_use_PrivateKey_file( + ctx, + key, + SSL_FILETYPE_PEM); + + if (res == 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + } + + if (crt) { + /* client cert key file defined, use it */ + res = SSL_CTX_use_certificate_file( + ctx, + crt, + SSL_FILETYPE_PEM); + + if (res == 0) { + ERR_print_errors_fp(stderr); + exit(EXIT_FAILURE); + } + } + + /* create a new connection to the server */ + conn = evhtp_connection_ssl_new(evbase, + addr ? : "127.0.0.1", + port, ctx); + evhtp_assert(conn != NULL); + + /* when the request has been completed, call https_resp_ */ + req = evhtp_request_new(https_resp_, evbase); + evhtp_assert(req != NULL); + + /* make a request context, 'GET / HTTP/1.1' */ + res = evhtp_make_request(conn, + req, + htp_method_GET, "/"); + evhtp_assert(res == 0); + + /* the loop will make the request and call https_resp_ + * when complete. + */ + event_base_loop(evbase, 0); + + /* free up all the resources */ + { + SSL_CTX_free(ctx); + evhtp_connection_free(conn); + evhtp_request_free(req); + event_base_free(evbase); + + free(crt); + free(key); + free(addr); + } + + return 0; +#else + log_error("Not compiled with SSL support, go away"); + return EXIT_FAILURE; +#endif +} /* main */ From bc52552641ef9d01713c6d8e9b41aa1c089091fe Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 20 Dec 2017 09:54:09 -0500 Subject: [PATCH 14/19] Update README.markdown --- README.markdown | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.markdown b/README.markdown index c965a8a..32a3f4b 100644 --- a/README.markdown +++ b/README.markdown @@ -10,6 +10,9 @@ This document describes details on using the evhtp API. This document is probably not very awesome, it's best to look at test.c to see advanced usage. +# Prebuilt Packages +[![Package Versions](https://repology.org/badge/vertical-allrepos/libevhtp.svg)](https://repology.org/metapackage/libevhtp) + ## Required Dependencies * [gcc](http://gcc.gnu.org/) * [Libevent2](http://libevent.org) From 8b68657e5315bb03b01a44dc480f417bda6bdbc0 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Wed, 3 Jan 2018 16:52:50 -0500 Subject: [PATCH 15/19] [#69] Fix potential out of bound write to p->buf As reported via https://github.com/criticalstack/libevhtp/issues/69, there is a slight chance that 1 byte can overflow parser->buf[STACK_MAX] when adding the extra '\0' Added the macro `HTP_SET_BUF(CH)` which does proper bounds checks before writing the final '\0'. --- parser.c | 327 +++++++++++++++++++++++++------------------------------ 1 file changed, 146 insertions(+), 181 deletions(-) diff --git a/parser.c b/parser.c index 5ab418e..9893bbf 100644 --- a/parser.c +++ b/parser.c @@ -703,6 +703,17 @@ get_method(const char * m, const size_t sz) return htp_method_UNKNOWN; } /* get_method */ +#define HTP_SET_BUF(CH) do { \ + if (evhtp_likely((p->buf_idx + 1) < PARSER_STACK_MAX)) { \ + p->buf[p->buf_idx++] = CH; \ + p->buf[p->buf_idx] = '\0'; \ + } else { \ + p->error = htparse_error_too_big; \ + return i + 1; \ + } \ +} while (0) + + size_t htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) { @@ -725,12 +736,6 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) log_debug("[%p] data[%zu] = %c (%x)", p, i, isprint(ch) ? ch : ' ', ch); - if (p->buf_idx >= PARSER_STACK_MAX) - { - p->error = htparse_error_too_big; - return i + 1; - } - p->total_bytes_read += 1; p->bytes_read += 1; @@ -754,33 +759,30 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) } - p->flags = 0; - p->error = htparse_error_none; - p->method = htp_method_UNKNOWN; - p->multipart = 0; - p->major = 0; - p->minor = 0; - p->content_len = 0; - p->orig_content_len = 0; - p->status = 0; - p->status_count = 0; - p->scheme_offset = NULL; - p->host_offset = NULL; - p->port_offset = NULL; - p->path_offset = NULL; - p->args_offset = NULL; + p->flags = 0; + p->error = htparse_error_none; + p->method = htp_method_UNKNOWN; + p->multipart = 0; + p->major = 0; + p->minor = 0; + p->content_len = 0; + p->orig_content_len = 0; + p->status = 0; + p->status_count = 0; + p->scheme_offset = NULL; + p->host_offset = NULL; + p->port_offset = NULL; + p->path_offset = NULL; + p->args_offset = NULL; res = hook_on_msg_begin_run(p, hooks); - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - if (evhtp_likely(p->type == htp_type_request)) - { + if (evhtp_likely(p->type == htp_type_request)) { p->state = s_method; - } else if (p->type == htp_type_response && ch == 'H') - { + } else if (p->type == htp_type_response && ch == 'H') { p->state = s_http_H; } else { log_debug("not type of request or response?"); @@ -824,8 +826,7 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) return i + 1; } - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); } ch = data[++i]; @@ -849,11 +850,10 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) break; case '[': /* Literal IPv6 address start. */ - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->host_offset = &p->buf[p->buf_idx]; + HTP_SET_BUF(ch); - p->state = s_host_ipv6; + p->host_offset = &p->buf[p->buf_idx]; + p->state = s_host_ipv6; break; default: if (!is_host_char(ch)) @@ -863,11 +863,12 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) return i + 1; } - p->host_offset = &p->buf[p->buf_idx]; - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_host; + p->host_offset = &p->buf[p->buf_idx]; + + HTP_SET_BUF(ch); + + p->state = s_host; break; } /* switch */ @@ -878,21 +879,21 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) case ' ': break; case '/': - p->path_offset = &p->buf[p->buf_idx]; + p->path_offset = &p->buf[p->buf_idx]; - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_after_slash_in_uri; + HTP_SET_BUF(ch); + + p->state = s_after_slash_in_uri; break; default: - c = (unsigned char)(ch | 0x20); + c = (unsigned char)(ch | 0x20); - if (c >= 'a' && c <= 'z') - { - p->scheme_offset = &p->buf[p->buf_idx]; - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_schema; + if (c >= 'a' && c <= 'z') { + p->scheme_offset = &p->buf[p->buf_idx]; + + HTP_SET_BUF(ch); + + p->state = s_schema; break; } @@ -908,10 +909,8 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) c = (unsigned char)(ch | 0x20); - if (c >= 'a' && c <= 'z') - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + if (c >= 'a' && c <= 'z') { + HTP_SET_BUF(ch); break; } @@ -954,13 +953,11 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) p->scheme_offset, (&p->buf[p->buf_idx] - p->scheme_offset)); - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); p->state = s_schema_slash; - if (res) - { + if (res) { p->error = htparse_error_user; return i + 1; } @@ -977,8 +974,7 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case '/': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); p->state = s_schema_slash_slash; break; @@ -992,33 +988,28 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case '/': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->host_offset = &p->buf[p->buf_idx]; + HTP_SET_BUF(ch); + p->host_offset = &p->buf[p->buf_idx]; - p->state = s_host; + p->state = s_host; break; default: - p->error = htparse_error_inval_schema; + p->error = htparse_error_inval_schema; return i + 1; } break; case s_host: - if (ch == '[') - { + if (ch == '[') { /* Literal IPv6 address start. */ - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->host_offset = &p->buf[p->buf_idx]; + HTP_SET_BUF(ch); + p->host_offset = &p->buf[p->buf_idx]; - p->state = s_host_ipv6; + p->state = s_host_ipv6; break; } - if (is_host_char(ch)) - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + if (is_host_char(ch)) { + HTP_SET_BUF(ch); break; } @@ -1041,11 +1032,10 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case ':': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->port_offset = &p->buf[p->buf_idx]; - p->state = s_port; + p->port_offset = &p->buf[p->buf_idx]; + p->state = s_port; break; case ' ': /* this technically should never happen, but we should @@ -1063,15 +1053,14 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) * we fallthrough to the next case. */ case '/': - p->path_offset = &p->buf[p->buf_idx]; + p->path_offset = &p->buf[p->buf_idx]; - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->state = s_after_slash_in_uri; + p->state = s_after_slash_in_uri; break; default: - p->error = htparse_error_inval_schema; + p->error = htparse_error_inval_schema; return i + 1; } /* switch */ @@ -1085,11 +1074,11 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) case s_host_ipv6: c = (unsigned char)(ch | 0x20); - if ((c >= 'a' && c <= 'f') || - (ch >= '0' && ch <= '9') || ch == ':' || ch == '.') - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + if ((c >= 'a' && c <= 'f') + || (ch >= '0' && ch <= '9') + || ch == ':' + || ch == '.') { + HTP_SET_BUF(ch); break; } @@ -1097,25 +1086,23 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) case ']': res = hook_host_run(p, hooks, p->host_offset, (&p->buf[p->buf_idx] - p->host_offset)); - if (res) - { + if (res) { p->error = htparse_error_user; return i + 1; } - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + + HTP_SET_BUF(ch); + p->state = s_host_done; break; default: p->error = htparse_error_inval_schema; return i + 1; - } + } /* switch */ break; case s_port: - if (ch >= '0' && ch <= '9') - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + if (ch >= '0' && ch <= '9') { + HTP_SET_BUF(ch); break; } @@ -1139,14 +1126,13 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) * we fallthrough to the next case. */ case '/': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->path_offset = &p->buf[p->buf_idx - 1]; + HTP_SET_BUF(ch); + p->path_offset = &p->buf[p->buf_idx - 1]; - p->state = s_after_slash_in_uri; + p->state = s_after_slash_in_uri; break; default: - p->error = htparse_error_inval_reqline; + p->error = htparse_error_inval_reqline; log_debug("[s_port] inval_reqline"); log_htparser__s_(p); @@ -1167,12 +1153,9 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) if (usual[ch >> 5] & (1 << (ch & 0x1f))) { - if (evhtp_likely((p->buf_idx + 1) < PARSER_STACK_MAX)) - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_check_uri; - } + HTP_SET_BUF(ch); + + p->state = s_check_uri; break; } @@ -1205,25 +1188,23 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) case '%': case '/': case '#': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_uri; + HTP_SET_BUF(ch); + p->state = s_uri; break; case '?': - res = hook_path_run(p, hooks, p->path_offset, - (&p->buf[p->buf_idx] - p->path_offset)); + res = hook_path_run(p, hooks, p->path_offset, + (&p->buf[p->buf_idx] - p->path_offset)); + + HTP_SET_BUF(ch); - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->args_offset = &p->buf[p->buf_idx]; - p->state = s_uri; + p->args_offset = &p->buf[p->buf_idx]; + p->state = s_uri; break; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->state = s_check_uri; + p->state = s_check_uri; break; } /* switch */ @@ -1241,10 +1222,9 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) do { log_debug("[%p] s_check_uri", p); - if (usual[ch >> 5] & (1 << (ch & 0x1f))) - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + HTP_SET_BUF(ch); } else { break; } @@ -1278,37 +1258,34 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) } break; case '/': - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_after_slash_in_uri; + HTP_SET_BUF(ch); + + p->state = s_after_slash_in_uri; break; case CR: - p->minor = 9; - p->buf_idx = 0; - p->state = s_almost_done; + p->minor = 9; + p->buf_idx = 0; + p->state = s_almost_done; break; case LF: - p->minor = 9; - p->buf_idx = 0; + p->minor = 9; + p->buf_idx = 0; - p->state = s_hdrline_start; + p->state = s_hdrline_start; break; case '?': - res = hook_path_run(p, hooks, - p->path_offset, - (&p->buf[p->buf_idx] - p->path_offset)); - - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + res = hook_path_run(p, hooks, + p->path_offset, + (&p->buf[p->buf_idx] - p->path_offset)); + HTP_SET_BUF(ch); - p->args_offset = &p->buf[p->buf_idx]; - p->state = s_uri; + p->args_offset = &p->buf[p->buf_idx]; + p->state = s_uri; break; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->state = s_uri; + p->state = s_uri; break; } /* switch */ @@ -1326,10 +1303,8 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) res = 0; do { - if (usual[ch >> 5] & (1 << (ch & 0x1f))) - { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + if (usual[ch >> 5] & (1 << (ch & 0x1f))) { + HTP_SET_BUF(ch); } else { break; } @@ -1377,20 +1352,17 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) * first question mark ("?") character and * terminated by a number sign ("#") character * or by the end of the URI. */ - if (!p->args_offset) - { + if (!p->args_offset) { res = hook_path_run(p, hooks, p->path_offset, (&p->buf[p->buf_idx] - p->path_offset)); - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->args_offset = &p->buf[p->buf_idx]; + HTP_SET_BUF(ch); + p->args_offset = &p->buf[p->buf_idx]; break; } /* Fall through. */ default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); break; } /* switch */ @@ -1660,16 +1632,15 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case CR: - p->state = s_hdrline_hdr_almost_done; + p->state = s_hdrline_hdr_almost_done; break; case LF: - p->state = s_hdrline_hdr_done; + p->state = s_hdrline_hdr_done; break; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->state = s_hdrline_hdr_key; + p->state = s_hdrline_hdr_key; break; } @@ -1738,14 +1709,13 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case CR: - p->state = s_hdrline_hdr_almost_done; + p->state = s_hdrline_hdr_almost_done; break; case LF: - p->state = s_hdrline_hdr_done; + p->state = s_hdrline_hdr_done; break; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); break; } @@ -1771,8 +1741,7 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) * to empty, set the state to hdrline_hdr_val, and * decrement the start byte counter. */ - p->buf[p->buf_idx++] = ' '; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(' '); p->state = s_hdrline_hdr_val; /* @@ -1785,12 +1754,11 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) /* never got a CR for an empty header, this is an * invalid state. */ - p->error = htparse_error_inval_hdr; + p->error = htparse_error_inval_hdr; return i + 1; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - p->state = s_hdrline_hdr_val; + HTP_SET_BUF(ch); + p->state = s_hdrline_hdr_val; break; } /* switch */ break; @@ -1927,11 +1895,10 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case LF: /* LF before CR? invalid */ - p->error = htparse_error_inval_hdr; + p->error = htparse_error_inval_hdr; return i + 1; default: - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); break; } /* switch */ @@ -1988,25 +1955,23 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) break; case LF: /* got LFLF? is this valid? */ - p->error = htparse_error_inval_hdr; + p->error = htparse_error_inval_hdr; return i + 1; case '\t': /* this is a multiline header value, we must go back to * reading as a header value */ - p->state = s_hdrline_hdr_val; + p->state = s_hdrline_hdr_val; break; default: - res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); + res = hook_hdr_val_run(p, hooks, p->buf, p->buf_idx); + p->buf_idx = 0; - p->buf_idx = 0; - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; + HTP_SET_BUF(ch); - p->state = s_hdrline_hdr_key; + p->state = s_hdrline_hdr_key; - if (res) - { + if (res) { p->error = htparse_error_user; return i + 1; } From 75574bab61f5b520e971330120f3ac4760c4a2d5 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Tue, 9 Jan 2018 09:17:50 -0500 Subject: [PATCH 16/19] [#72] Fix for oob read from htparser_get_strerror While the API was never effected by this, I guess a user who had set their own parser error could have triggered this read. But alas, a bug is a bug. We don't look for error + 1 in the error string table, we just look for error > last_enum. --- parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/parser.c b/parser.c index 9893bbf..4bb7733 100644 --- a/parser.c +++ b/parser.c @@ -428,7 +428,7 @@ htparser_get_strerror(htparser * p) { htpparse_error e = htparser_get_error(p); - if (e > (htparse_error_generic + 1)) + if (e > htparse_error_generic) { return "htparse_no_such_error"; } From 4cb782db77b0c2a1086250477ee66709b41828dc Mon Sep 17 00:00:00 2001 From: Ultima1252 Date: Thu, 11 Jan 2018 08:07:12 -0800 Subject: [PATCH 17/19] Update evhtp.c --- evhtp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evhtp.c b/evhtp.c index a6736b7..ff7e76b 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2373,14 +2373,14 @@ htp__connection_writecb_(struct bufferevent * bev, void * arg) return; } - /* run user-hook for on_write callback before further analysis */ - htp__hook_connection_write_(conn); - /* connection is in a paused state, no further processing yet */ if ((conn->flags & EVHTP_CONN_FLAG_PAUSED)) { return; } + + /* run user-hook for on_write callback before further analysis */ + htp__hook_connection_write_(conn); if (conn->flags & EVHTP_CONN_FLAG_WAITING) { @@ -3043,7 +3043,7 @@ evhtp_connection_pause(evhtp_connection_t * c) HTP_FLAG_ON(c, EVHTP_CONN_FLAG_PAUSED); - bufferevent_disable(c->bev, EV_READ | EV_WRITE); + bufferevent_disable(c->bev, EV_READ); return; } From 4cae81a9dfa941af12875a8eb410eb474b62b110 Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 15 Jan 2018 17:48:49 -0500 Subject: [PATCH 18/19] Formatting --- evhtp.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/evhtp.c b/evhtp.c index ff7e76b..6eaf319 100644 --- a/evhtp.c +++ b/evhtp.c @@ -320,7 +320,7 @@ htp__strndup_(const char * str, size_t len) void evhtp_set_mem_functions(void *(*mallocfn_)(size_t len), void *(*reallocfn_)(void * p, size_t sz), - void (* freefn_)(void * p)) + void (*freefn_)(void * p)) { #ifndef EVHTP_DISABLE_MEMFUNCTIONS malloc_ = mallocfn_; @@ -2378,7 +2378,7 @@ htp__connection_writecb_(struct bufferevent * bev, void * arg) { return; } - + /* run user-hook for on_write callback before further analysis */ htp__hook_connection_write_(conn); @@ -2929,8 +2929,8 @@ 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 = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); + cfg = connection->htp->ssl_cfg; + sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen); SSL_set_timeout(sess, cfg->scache_timeout); @@ -2955,8 +2955,8 @@ htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, { 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) { @@ -4837,7 +4837,7 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) EC_KEY * ecdh = NULL; int nid = 0; - nid = OBJ_sn2nid(cfg->named_curve); + nid = OBJ_sn2nid(cfg->named_curve); if (nid == 0) { log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve); From 109a9272793de59e198626206ca268859b4be5ec Mon Sep 17 00:00:00 2001 From: Nathan French Date: Mon, 15 Jan 2018 17:53:56 -0500 Subject: [PATCH 19/19] Release v1.2.16, see ChangeLog for details --- ChangeLog | 16 ++++++++++++++++ cmake/version.cmake | 2 +- include/evhtp/evhtp.h | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8ada5aa..3c4ffc3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +v1.2.16 + o Added various SSL utility functions (ab190e1 Nathan French) + o Fix compilation with shared libraries (6075565 Vincent Bernat) + o Extensive example of streaming data without hogging memory (8f50b83 Nathan French) + o Added EVHTP_FLAG_ENABLE_ALL to enable all socket flags (756a7e2 Nathan French) + o only warn if setsockopt fails on EOPNOTSUPP (e5a3bdf Nathan French) + o sslutil API updates [documentation / parsers / x-hdr helpers] (22207ad Nathan French) + o OpenSSL 1.1.0 updates (load_*/_init/_add*) (3819073 Nathan French) + o example_https_server now uses htp_sslutil_verify2opts (086afd1 Nathan French) + o added example_https_client.c (933febf Nathan French) + o [#69] Fix potential out of bound write to p->buf (8b68657 Nathan French) + o [#72] Fix for oob read from htparser_get_strerror (75574ba Nathan French) + o [#70] Do not disable EV_WRITE when pausing requests; The assumption + that libevent would automatically start transferring pending data was + wrong. (4cb782db77 Ultima1252) + v1.2.15 o deprecated unset_hook and set_hook / cleanup (f1d2bd1 Nathan French) o updated travis configuration (45003e1 Nathan French) diff --git a/cmake/version.cmake b/cmake/version.cmake index f88c98f..80ecbb2 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 15) +set (PROJECT_PATCH_VERSION 16) 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 383aebd..344f58c 100644 --- a/include/evhtp/evhtp.h +++ b/include/evhtp/evhtp.h @@ -192,7 +192,7 @@ 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.15" +#define EVHTP_VERSION "1.2.16" #define EVHTP_VERSION_MAJOR 1 #define EVHTP_VERSION_MINOR 2 #define EVHTP_VERSION_PATCH 15