diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index 5d192567..34a9bac6 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c @@ -59,7 +59,7 @@ #if HTTPS_SUPPORT /* initialize security aspects of the HTTPS daemon */ static int -MHDS_init (struct MHD_Daemon *daemon) +MHD_TLS_init (struct MHD_Daemon *daemon) { int i; priority_st st; @@ -305,7 +305,7 @@ gnutls_push_param_adapter (void *connection, */ #if HTTPS_SUPPORT static void * -MHDS_handle_connection (void *data) +MHD_TLS_handle_connection (void *data) { struct MHD_Connection *con = data; @@ -489,7 +489,7 @@ MHD_accept_connection (struct MHD_Daemon *daemon) #if HTTPS_SUPPORT if (daemon->options & MHD_USE_SSL) res_thread_create = pthread_create (&connection->pid, NULL, - &MHDS_handle_connection, + &MHD_TLS_handle_connection, connection); else #endif @@ -767,11 +767,11 @@ MHD_select_thread (void *cls) * @return NULL on error, handle to daemon on success */ struct MHD_Daemon * -MHD_start_daemon (unsigned int options, - unsigned short port, - MHD_AcceptPolicyCallback apc, - void *apc_cls, - MHD_AccessHandlerCallback dh, void *dh_cls, ...) +MHD_start_daemon_va (unsigned int options, + unsigned short port, + MHD_AcceptPolicyCallback apc, + void *apc_cls, + MHD_AccessHandlerCallback dh, void *dh_cls, va_list ap) { const int on = 1; struct MHD_Daemon *retVal; @@ -783,7 +783,6 @@ MHD_start_daemon (unsigned int options, struct sockaddr_in6 servaddr6; const struct sockaddr *servaddr; socklen_t addrlen; - va_list ap; enum MHD_OPTION opt; if ((port == 0) || (dh == NULL)) @@ -879,7 +878,7 @@ MHD_start_daemon (unsigned int options, } #endif /* initializes the argument pointer variable */ - va_start (ap, dh_cls); + /* * loop through daemon options */ @@ -905,6 +904,10 @@ MHD_start_daemon (unsigned int options, retVal->per_ip_connection_limit = va_arg (ap, unsigned int); break; #if HTTPS_SUPPORT + case MHD_OPTION_PROTOCOL_VERSION: + _set_priority (&retVal->priority_cache->protocol, + va_arg (ap, const int *)); + break; case MHD_OPTION_HTTPS_KEY_PATH: retVal->https_key_path = va_arg (ap, const char *); break; @@ -925,13 +928,17 @@ MHD_start_daemon (unsigned int options, _set_priority (&retVal->priority_cache->cipher, va_arg (ap, const int *)); break; + case MHD_OPTION_MAC_ALGO: + _set_priority (&retVal->priority_cache->mac, + va_arg (ap, const int *)); + break; #endif default: #if HAVE_MESSAGES if (opt > MHD_HTTPS_OPTION_START && opt < MHD_HTTPS_OPTION_END) { fprintf (stderr, - "Error: HTTPS option given while compiling without HTTPS support\n"); + "Error: HTTPS option %d passed to non HTTPS daemon\n", opt); } else { @@ -942,18 +949,19 @@ MHD_start_daemon (unsigned int options, abort (); } } + #if HTTPS_SUPPORT /* initialize HTTPS daemon certificate aspects & send / recv functions */ - if (options & MHD_USE_SSL && MHD_NO == MHDS_init (retVal)) + if (options & MHD_USE_SSL && MHD_NO == MHD_TLS_init (retVal)) { #if HAVE_MESSAGES - MHD_DLOG (retVal, "Failed to initialize MHDS\n", STRERROR (errno)); + MHD_DLOG (retVal, "Failed to initialize HTTPS daemon\n"); #endif free (retVal); return NULL; } #endif - va_end (ap); + if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (options & MHD_USE_SELECT_INTERNALLY))) @@ -972,6 +980,36 @@ MHD_start_daemon (unsigned int options, return retVal; } +struct MHD_Daemon * +MHD_start_daemon (unsigned int options, + unsigned short port, + MHD_AcceptPolicyCallback apc, + void *apc_cls, + MHD_AccessHandlerCallback dh, void *dh_cls, ...){ + + int ret; + va_list ap; + va_start (ap, dh_cls); + ret = MHD_start_daemon_va (options, + port, + apc, + apc_cls, + dh, dh_cls, ap); + va_end (ap); + return ret; +} + +/** + * Start a webserver on the given port. + * + * @param port port to bind to + * @param apc callback to call to check which clients + * will be allowed to connect + * @param apc_cls extra argument to apc + * @param dh default handler for all URIs + * @param dh_cls extra argument to dh + * @return NULL on error, handle to daemon on success + */ /** * Shutdown an http daemon. */ diff --git a/src/daemon/https/tls/gnutls_alert.c b/src/daemon/https/tls/gnutls_alert.c index 75f9dcd2..dfa35f85 100644 --- a/src/daemon/https/tls/gnutls_alert.c +++ b/src/daemon/https/tls/gnutls_alert.c @@ -144,8 +144,8 @@ gnutls_alert_send (gnutls_session_t session, gnutls_alert_level_t level, * * Returns an alert depending on the error code returned by a gnutls * function. All alerts sent by this function should be considered fatal. - * The only exception is when err == GNUTLS_E_REHANDSHAKE, where a warning - * alert should be sent to the peer indicating that no renegotiation will + * The only exception is when err == GNUTLS_E_REHANDSHAKE, where a warning + * alert should be sent to the peer indicating that no renegotiation will * be performed. * * If there is no mapping to a valid alert the alert to indicate internal error diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index ad8b1e77..cb6993c8 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h @@ -379,6 +379,15 @@ enum MHD_OPTION */ MHD_OPTION_HTTPS_MEM_CERT, + /* + * SSL/TLS protocol version + * + * Memory pointer to a zero terminated int array representing the + * protocol versions to this server should support. Unsupported + * requests will be droped by the server. + */ + MHD_OPTION_PROTOCOL_VERSION, + /* * Memory pointer to a zero terminated int array representing the * cipher priority order to which the HTTPS daemon should adhere. @@ -703,6 +712,18 @@ typedef int * terminated with MHD_OPTION_END). * @return NULL on error, handle to daemon on success */ +struct MHD_Daemon * +MHD_start_daemon_va (unsigned int options, + unsigned short port, + MHD_AcceptPolicyCallback apc, + void *apc_cls, + MHD_AccessHandlerCallback dh, void *dh_cls, va_list ap); + + +/* + * Variadic version of MHD_start_daemon_va. This function will delegate calls + * to MHD_start_daemon_va() once argument list is analyzed. + */ struct MHD_Daemon *MHD_start_daemon (unsigned int flags, unsigned short port, MHD_AcceptPolicyCallback apc, diff --git a/src/testcurl/Makefile.am b/src/testcurl/Makefile.am index cb8172b0..f5c9b236 100644 --- a/src/testcurl/Makefile.am +++ b/src/testcurl/Makefile.am @@ -38,7 +38,6 @@ libcurl_version_check_a_CPPFLAGS = \ -I$(top_srcdir)/src/daemon/https \ $(LIBCURL_CPPFLAGS) - daemontest_get_SOURCES = \ daemontest_get.c daemontest_get_LDADD = \ diff --git a/src/testcurl/curl_version_check.c b/src/testcurl/curl_version_check.c index 20587bf2..8847d035 100644 --- a/src/testcurl/curl_version_check.c +++ b/src/testcurl/curl_version_check.c @@ -89,7 +89,7 @@ curl_check_version (const char *req_version) fprintf (stderr, "curl version: %s\n", ver); #endif /* - * this call relies on the cURL string to be of the format : + * this call relies on the cURL string to be of the exact following format : * 'libcurl/7.16.4 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/0.6.5' OR * 'libcurl/7.18.2 GnuTLS/2.4.0 zlib/1.2.3.3 libidn/0.6.5' */ @@ -118,7 +118,7 @@ curl_check_version (const char *req_version) * TODO use curl version string to assert use of gnutls */ #if HTTPS_SUPPORT - ssl_ver = strchr (curl_ver, '\ ') + 1; + ssl_ver = strchr (curl_ver, ' ') + 1; if (strncmp ("GnuTLS", ssl_ver, strlen ("GNUtls")) == 0) { @@ -132,7 +132,7 @@ curl_check_version (const char *req_version) } else { - fprintf (stderr, "Error: unrecognized curl ssl library\n", req_ssl_ver); + fprintf (stderr, "Error: unrecognized curl ssl library\n"); return -1; } diff --git a/src/testcurl/https/Makefile.am b/src/testcurl/https/Makefile.am index 827b0e7c..73698996 100644 --- a/src/testcurl/https/Makefile.am +++ b/src/testcurl/https/Makefile.am @@ -12,7 +12,7 @@ $(LIBCURL_CPPFLAGS) check_PROGRAMS = \ tls_session_time_out_test \ tls_cipher_change_test \ -mhds_get_test \ +tls_daemon_options_test \ tls_alert_test \ tls_authentication_test \ mhds_multi_daemon_test \ @@ -20,6 +20,13 @@ mhds_session_info_test TESTS = $(check_PROGRAMS) +tls_daemon_options_test_SOURCES = \ + tls_daemon_options_test.c +tls_daemon_options_test_LDADD = \ + $(top_builddir)/src/testcurl/libcurl_version_check.a \ + $(top_builddir)/src/daemon/libmicrohttpd.la \ + @LIBCURL@ + tls_session_time_out_test_SOURCES = \ tls_session_time_out_test.c tls_session_time_out_test_LDADD = \ @@ -45,13 +52,6 @@ tls_authentication_test_LDADD = \ $(top_builddir)/src/daemon/libmicrohttpd.la \ @LIBCURL@ -mhds_get_test_SOURCES = \ - mhds_get_test.c -mhds_get_test_LDADD = \ - $(top_builddir)/src/testcurl/libcurl_version_check.a \ - $(top_builddir)/src/daemon/libmicrohttpd.la \ - @LIBCURL@ - mhds_session_info_test_SOURCES = \ mhds_session_info_test.c mhds_session_info_test_LDADD = \ diff --git a/src/testcurl/https/mhds_get_test.c b/src/testcurl/https/tls_daemon_options_test.c similarity index 69% rename from src/testcurl/https/mhds_get_test.c rename to src/testcurl/https/tls_daemon_options_test.c index bac6b895..95accebb 100644 --- a/src/testcurl/https/mhds_get_test.c +++ b/src/testcurl/https/tls_daemon_options_test.c @@ -123,11 +123,11 @@ http_ahc (void *cls, struct MHD_Connection *connection, * @param test_fd: file to attempt transfering */ static int -test_daemon_get (FILE * test_fd, char *cipher_suite, int proto_version) +test_https_transfer (FILE * test_fd, char * cipher_suite, int proto_version) { CURL *c; - struct CBC cbc; CURLcode errornum; + struct CBC cbc; char *doc_path; char url[255]; struct stat statb; @@ -177,8 +177,8 @@ test_daemon_get (FILE * test_fd, char *cipher_suite, int proto_version) #endif curl_easy_setopt (c, CURLOPT_URL, url); curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - curl_easy_setopt (c, CURLOPT_TIMEOUT, 2L); - curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 2L); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 5L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); curl_easy_setopt (c, CURLOPT_FILE, &cbc); @@ -220,30 +220,77 @@ test_daemon_get (FILE * test_fd, char *cipher_suite, int proto_version) return 0; } -/* perform a HTTP GET request via SSL/TLS */ -int -test_secure_get (FILE * test_fd, char *cipher_suite, int proto_version) +FILE * +setupTestFile () { - int ret; - struct MHD_Daemon *d; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_END); + FILE *test_fd; - if (d == NULL) + if (NULL == (test_fd = fopen (test_file_name, "w+"))) + { + fprintf (stderr, "Error: failed to open `%s': %s\n", + test_file_name, strerror (errno)); + return NULL; + } + if (fwrite (test_file_data, sizeof (char), strlen (test_file_data), test_fd) + != strlen (test_file_data)) + { + fprintf (stderr, "Error: failed to write `%s. %s'\n", + test_file_name, strerror (errno)); + return NULL; + } + if (fflush (test_fd)) + { + fprintf (stderr, "Error: failed to flush test file stream. %s\n", + strerror (errno)); + return NULL; + } + + return test_fd; +} + +static int +setup (struct MHD_Daemon **d, enum MHD_OPTION option, void * value ) +{ + *d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | + MHD_USE_DEBUG, 42433, + NULL, NULL, &http_ahc, NULL, + MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, + MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, + option, value, MHD_OPTION_END); + + if (*d == NULL) { fprintf (stderr, MHD_E_SERVER_INIT); return -1; } - ret = test_daemon_get (test_fd, cipher_suite, proto_version); + return 0; +} + +static void +teardown (struct MHD_Daemon *d) +{ MHD_stop_daemon (d); +} + +int +test_wrap (int + (*test) (FILE * test_fd, char *cipher_suite, int proto_version), + FILE * test_fd, char *cipher_suite, int proto_version, + enum MHD_OPTION option, void * value) +{ + int ret; + struct MHD_Daemon *d; + + if (setup (&d, option, value) != 0) + return -1; + ret = test (test_fd, cipher_suite, proto_version); + teardown (d); return ret; } +/* perform a HTTP GET request via SSL/TLS */ + /* test loading of key & certificate files */ int test_file_certificates (FILE * test_fd, char *cipher_suite, int proto_version) @@ -275,21 +322,13 @@ test_file_certificates (FILE * test_fd, char *cipher_suite, int proto_version) fclose (key_fd); fclose (cert_fd); - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_KEY_PATH, key_path, - MHD_OPTION_HTTPS_CERT_PATH, cert_path, - MHD_OPTION_END); - if (d == NULL) { fprintf (stderr, MHD_E_SERVER_INIT); return -1; } - ret = test_daemon_get (test_fd, cipher_suite, proto_version); - MHD_stop_daemon (d); + ret = test_https_transfer (test_fd, cipher_suite, proto_version); free (cur_dir); remove (cert_path); @@ -298,123 +337,54 @@ test_file_certificates (FILE * test_fd, char *cipher_suite, int proto_version) } int -test_cipher_option (FILE * test_fd, char *cipher_suite, int proto_version) +test_protocol_version (FILE * test_fd, char *cipher_suite, + int curl_proto_version) { + CURL *c; + CURLcode errornum; - int ret; - int ciper[] = { MHD_GNUTLS_CIPHER_3DES_CBC, 0 }; - struct MHD_Daemon *d; - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_CIPHER_ALGORITHM, ciper, MHD_OPTION_END); + c = curl_easy_init (); +#ifdef DEBUG + curl_easy_setopt (c, CURLOPT_VERBOSE, 1); +#endif + curl_easy_setopt (c, CURLOPT_URL, "https://localhost:42433/"); + curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); + curl_easy_setopt (c, CURLOPT_TIMEOUT, 5L); + curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); - if (d == NULL) + /* TLS options */ + curl_easy_setopt (c, CURLOPT_SSLVERSION, curl_proto_version); + curl_easy_setopt (c, CURLOPT_SSL_CIPHER_LIST, cipher_suite); + + curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0); + curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); + + /* NOTE: use of CONNECTTIMEOUT without also + setting NOSIGNAL results in really weird + crashes on my system! */ + curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1); + + /* assert daemon rejected request */ + if (CURLE_OK == (errornum = curl_easy_perform (c))) { - fprintf (stderr, MHD_E_SERVER_INIT); + fprintf (stderr, "curl_easy_perform failed: `%s'\n", + curl_easy_strerror (errornum)); + curl_easy_cleanup (c); return -1; } - ret = test_daemon_get (test_fd, cipher_suite, proto_version); - - MHD_stop_daemon (d); - return ret; -} - -int -test_kx_option (FILE * test_fd, char *cipher_suite, int proto_version) -{ - - int ret; - int ciper[] = { MHD_GNUTLS_CIPHER_3DES_CBC, 0 }; - int kx[] = { MHD_GNUTLS_KX_DHE_RSA, 0 }; - struct MHD_Daemon *d; - - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_KX_PRIORITY, kx, - MHD_OPTION_CIPHER_ALGORITHM, ciper, MHD_OPTION_END); - - if (d == NULL) - { - fprintf (stderr, MHD_E_SERVER_INIT); - return -1; - } - - ret = test_daemon_get (test_fd, cipher_suite, proto_version); - - MHD_stop_daemon (d); - return ret; -} - -int -test_mac_option (FILE * test_fd, char *cipher_suite, int proto_version) -{ - - int ret; - int mac[] = { MHD_GNUTLS_MAC_SHA1, 0 }; - struct MHD_Daemon *d; - - d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | - MHD_USE_DEBUG, 42433, - NULL, NULL, &http_ahc, NULL, - MHD_OPTION_HTTPS_MEM_KEY, srv_key_pem, - MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, - MHD_OPTION_MAC_ALGO, mac, MHD_OPTION_END); - - if (d == NULL) - { - fprintf (stderr, MHD_E_SERVER_INIT); - return -1; - } - - ret = test_daemon_get (test_fd, cipher_suite, proto_version); - - MHD_stop_daemon (d); - return ret; + return 0; } /* setup a temporary transfer test file */ -FILE * -setupTestFile () -{ - FILE *test_fd; - - if (NULL == (test_fd = fopen (test_file_name, "w+"))) - { - fprintf (stderr, "Error: failed to open `%s': %s\n", - test_file_name, strerror (errno)); - return NULL; - } - if (fwrite (test_file_data, sizeof (char), strlen (test_file_data), test_fd) - != strlen (test_file_data)) - { - fprintf (stderr, "Error: failed to write `%s. %s'\n", - test_file_name, strerror (errno)); - return NULL; - } - if (fflush (test_fd)) - { - fprintf (stderr, "Error: failed to flush test file stream. %s\n", - strerror (errno)); - return NULL; - } - - return test_fd; -} - int main (int argc, char *const *argv) { FILE *test_fd; unsigned int errorCount = 0; - /* gnutls_global_set_log_level(11); */ + gnutls_global_set_log_level(11); if (curl_check_version (MHD_REQ_CURL_VERSION)) { @@ -433,20 +403,54 @@ main (int argc, char *const *argv) return -1; } - errorCount += - test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_TLSv1); - errorCount += - test_secure_get (test_fd, "AES256-SHA", CURL_SSLVERSION_SSLv3); - errorCount += - test_file_certificates (test_fd, "AES256-SHA", CURL_SSLVERSION_SSLv3); - /* TODO resolve cipher setting issue when compiling against GNU TLS */ + int mac[] = {MHD_GNUTLS_MAC_SHA1, 0}; + int p [] = {MHD_GNUTLS_SSL3, 0}; + int cipher[] = { MHD_GNUTLS_CIPHER_3DES_CBC, 0 }; + int kx[] = { MHD_GNUTLS_KX_DHE_RSA, 0 }; + + // errorCount += -// test_cipher_option (test_fd, "DES-CBC3-SHA", CURL_SSLVERSION_TLSv1); +// test_wrap (&test_https_transfer, test_fd, "AES256-SHA", +// CURL_SSLVERSION_TLSv1, MHD_OPTION_END, 0); // errorCount += -// test_kx_option (test_fd, "EDH-RSA-DES-CBC3-SHA", CURL_SSLVERSION_TLSv1); +// test_wrap (&test_file_certificates, test_fd, "AES256-SHA", +// CURL_SSLVERSION_TLSv1, MHD_OPTION_END, 0); +// +// errorCount += +// test_wrap (&test_protocol_version, test_fd, "AES256-SHA", +// CURL_SSLVERSION_TLSv1, MHD_OPTION_PROTOCOL_VERSION, p); +// +// errorCount += +// test_wrap (&test_https_transfer, test_fd, "DES-CBC3-SHA", +// CURL_SSLVERSION_TLSv1, MHD_OPTION_CIPHER_ALGORITHM, cipher); + + errorCount += + test_wrap (&test_https_transfer, test_fd, "AES256-SHA", + CURL_SSLVERSION_TLSv1, MHD_OPTION_MAC_ALGO, mac); + + // errorCount += + // test_wrap (&test_https_transfer, test_fd, "EDH-RSA-DES-CBC3-SHA", + // CURL_SSLVERSION_TLSv1, MHD_OPTION_KX_PRIORITY, kx); + + /*gnutls_mac_algorithm_t mac[] = { + {MHD_GNUTLS_MAC_MD5, 0}, 0}; + gnutls_mac_algorithm_t * cur_mac; + + for ( cur_mac = &mac[0]; (*cur_mac) != 0; cur_mac++ ){ + option[0] = MHD_GNUTLS_MAC_SHA1; + errorCount += + test_wrap (&test_https_transfer, test_fd, "AES256-SHA", + CURL_SSLVERSION_TLSv1, MHD_OPTION_MAC_ALGO, option); + }*/ + + if (errorCount != 0) fprintf (stderr, "Failed test: %s.\n", argv[0]); + else + { + fprintf (stderr, "ok\n"); + } curl_global_cleanup (); fclose (test_fd); diff --git a/src/testcurl/https/tls_session_time_out_test.c b/src/testcurl/https/tls_session_time_out_test.c index 5650be55..846bdd10 100644 --- a/src/testcurl/https/tls_session_time_out_test.c +++ b/src/testcurl/https/tls_session_time_out_test.c @@ -128,7 +128,6 @@ test_tls_session_time_out (gnutls_session_t session) sleep (TIME_OUT + 1); - /* check that server has closed the connection */ /* TODO better RST trigger */ if (send (sd, "", 1, 0) == 0)