/
githubmirror
/
libmicrohttpd
Обзор
Документация
Войти
/
githubmirror
/
libmicrohttpd
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/testcurl/https/test_tls_options.c
462 строки
14 KB
Christian Grothoff
expand test suite, add CI/CD logic as done in other GNUnet projects
28 июл 2026, 13:36
Не верифицирован
28 июл 2026, 13:36
e34baa3
Код
Авторство
О чём код?
/* This file is part of libmicrohttpd Copyright (C) 2007, 2016 Christian Grothoff Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) libmicrohttpd is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. libmicrohttpd is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with libmicrohttpd; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** * @file test_tls_options.c * @brief Testcase for libmicrohttpd HTTPS TLS version match/mismatch * @author Sagie Amir * @author Karlson2k (Evgeny Grin) */ #include "platform.h" #include "microhttpd.h" #include <curl/curl.h> #ifdef MHD_HTTPS_REQUIRE_GCRYPT #include <gcrypt.h> #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ #include "tls_test_common.h" #include "tls_test_keys.h" /* Turn any MHD_PANIC() or failing mhd_assert() reached from this test into a marked, classifiable test error (TESTING.md, P5). */ #include "mhd_panic_tripwire.h" /* * HTTP access handler call back * used to query negotiated security parameters */ static enum MHD_Result simple_ahc (void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **req_cls) { struct MHD_Response *response; enum MHD_Result ret; (void) cls; (void) url; (void) method; (void) version; /* Unused. Silent compiler warning. */ (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ if (NULL == *req_cls) { *req_cls = (void *) &simple_ahc; return MHD_YES; } response = MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (EMPTY_PAGE), EMPTY_PAGE); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); MHD_destroy_response (response); return ret; } enum check_result { CHECK_RES_OK = 0, CHECK_RES_ERR = 1, CHECK_RES_MHD_START_FAILED = 17, CHECK_RES_CURL_TLS_INIT_FAIL = 18, CHECK_RES_CURL_TLS_CONN_FAIL = 19, CHECK_RES_HARD_ERROR = 99 }; static enum check_result check_tls_match_inner (enum know_gnutls_tls_id tls_ver_mhd, enum know_gnutls_tls_id tls_ver_libcurl, uint16_t *pport, struct MHD_Daemon **d_ptr, struct CBC *pcbc, CURL **c_ptr) { CURLcode errornum; char url[256]; int libcurl_tls_set; CURL *c; struct MHD_Daemon *d; /* setup test */ d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS | MHD_USE_ERROR_LOG, *pport, NULL, NULL, &simple_ahc, NULL, MHD_OPTION_HTTPS_PRIORITIES, priorities_map[tls_ver_mhd], MHD_OPTION_HTTPS_MEM_KEY, srv_self_signed_key_pem, MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, MHD_OPTION_END); fflush (stderr); fflush (stdout); *d_ptr = d; if (d == NULL) { fprintf (stderr, "MHD_start_daemon() with %s failed.\n", tls_names[tls_ver_mhd]); return CHECK_RES_MHD_START_FAILED; } if (0 == *pport) { const union MHD_DaemonInfo *dinfo; dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); if ((NULL == dinfo) || (0 == dinfo->port) ) { fprintf (stderr, "MHD_get_daemon_info() failed.\n"); return CHECK_RES_ERR; } *pport = dinfo->port; /* Use the same port for rest of the checks */ } if (0 != gen_test_uri (url, sizeof (url), *pport)) { fprintf (stderr, "failed to generate URI.\n"); return CHECK_RES_CURL_TLS_INIT_FAIL; } c = curl_easy_init (); fflush (stderr); fflush (stdout); *c_ptr = c; if (NULL == c) { fprintf (stderr, "curl_easy_init() failed.\n"); return CHECK_RES_HARD_ERROR; } #ifdef _DEBUG curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); #endif if ((CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_URL, url))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_WRITEDATA, pcbc))) || /* TLS options */ /* currently skip any peer authentication */ (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) || (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)))) { fflush (stderr); fflush (stdout); fprintf (stderr, "Error setting libcurl option: %s.\n", curl_easy_strerror (errornum)); return CHECK_RES_HARD_ERROR; } libcurl_tls_set = 0; #if CURL_AT_LEAST_VERSION (7,54,0) if (CURL_SSLVERSION_MAX_DEFAULT != libcurl_tls_max_vers_map[tls_ver_libcurl]) { errornum = curl_easy_setopt (c, CURLOPT_SSLVERSION, libcurl_tls_vers_map[tls_ver_libcurl] | libcurl_tls_max_vers_map[tls_ver_libcurl]); if (CURLE_OK == errornum) libcurl_tls_set = 1; else { fprintf (stderr, "Error setting libcurl TLS version range: " "%s.\nRetrying with minimum TLS version only.\n", curl_easy_strerror (errornum)); } } #endif /* CURL_AT_LEAST_VERSION(7,54,0) */ if (! libcurl_tls_set && (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_SSLVERSION, libcurl_tls_vers_map[tls_ver_libcurl])))) { fprintf (stderr, "Error setting libcurl minimum TLS version: %s.\n", curl_easy_strerror (errornum)); return CHECK_RES_CURL_TLS_INIT_FAIL; } errornum = curl_easy_perform (c); fflush (stderr); fflush (stdout); if (CURLE_OK != errornum) { if ((CURLE_SSL_CONNECT_ERROR == errornum) || (CURLE_SSL_CIPHER == errornum)) { fprintf (stderr, "libcurl request failed due to TLS error: '%s'\n", curl_easy_strerror (errornum)); return CHECK_RES_CURL_TLS_CONN_FAIL; } else { fprintf (stderr, "curl_easy_perform failed: '%s'\n", curl_easy_strerror (errornum)); return CHECK_RES_ERR; } } return CHECK_RES_OK; } /** * negotiate a secure connection with server with specific TLS versions * set for MHD and for libcurl */ static enum check_result check_tls_match (enum know_gnutls_tls_id tls_ver_mhd, enum know_gnutls_tls_id tls_ver_libcurl, uint16_t *pport) { CURL *c; struct CBC cbc; enum check_result ret; struct MHD_Daemon *d; if (NULL == (cbc.buf = malloc (sizeof (char) * 255))) return CHECK_RES_HARD_ERROR; cbc.size = 255; cbc.pos = 0; d = NULL; c = NULL; ret = check_tls_match_inner (tls_ver_mhd, tls_ver_libcurl, pport, &d, &cbc, &c); fflush (stderr); fflush (stdout); if (NULL != d) MHD_stop_daemon (d); if (NULL != c) curl_easy_cleanup (c); free (cbc.buf); return ret; } static unsigned int test_first_supported_versions (void) { enum know_gnutls_tls_id ver_for_check; /**< TLS version used for test */ const gnutls_protocol_t *vers_list; /**< The list of GnuTLS supported TLS versions */ uint16_t port; if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) port = 0; /* Use system automatic assignment */ else port = 3080; /* Use predefined port, may break parallel testing of another MHD build */ vers_list = gnutls_protocol_list (); if (NULL == vers_list) { fprintf (stderr, "Error getting GnuTLS supported TLS versions"); return 99; } for (ver_for_check = KNOWN_TLS_MIN; KNOWN_TLS_MAX >= ver_for_check; ++ver_for_check) { const gnutls_protocol_t *ver_ptr; /**< The pointer to the position on the @a vers_list */ enum check_result res; for (ver_ptr = vers_list; 0 != *ver_ptr; ++ver_ptr) { if (ver_for_check == (enum know_gnutls_tls_id) *ver_ptr) break; } if (0 == *ver_ptr) { printf ("%s is not supported by GnuTLS, skipping.\n\n", tls_names[ver_for_check]); fflush (stdout); continue; } if (CURL_SSLVERSION_LAST == libcurl_tls_vers_map[ver_for_check]) { printf ("%s is not supported by libcurl, skipping.\n\n", tls_names[ver_for_check]); fflush (stdout); continue; } /* Found some TLS version that supported by GnuTLS and should be supported by libcurl (but in practice support depends on used TLS library) */ if (KNOWN_TLS_MIN != ver_for_check) printf ("\n"); printf ("Starting check with MHD set to '%s' and " "libcurl set to '%s' (successful connection is expected)...\n", tls_names[ver_for_check], tls_names[ver_for_check]); fflush (stdout); /* Check with MHD and libcurl set to the same TLS version */ res = check_tls_match (ver_for_check, ver_for_check, &port); if (CHECK_RES_HARD_ERROR == res) { fprintf (stderr, "Hard error. Test stopped.\n"); fflush (stderr); return 99; } else if (CHECK_RES_ERR == res) { printf ("Test failed.\n"); fflush (stdout); return 2; } else if (CHECK_RES_MHD_START_FAILED == res) { printf ("Skipping '%s' as MHD cannot be started with this setting.\n", tls_names[ver_for_check]); fflush (stdout); continue; } else if (CHECK_RES_CURL_TLS_INIT_FAIL == res) { printf ("Skipping '%s' as libcurl rejected this setting.\n", tls_names[ver_for_check]); fflush (stdout); continue; } else if (CHECK_RES_CURL_TLS_CONN_FAIL == res) { printf ("Skipping '%s' as it is not supported by current libcurl " "and GnuTLS combination.\n", tls_names[ver_for_check]); fflush (stdout); continue; } printf ("Connection succeeded for MHD set to '%s' and " "libcurl set to '%s'.\n\n", tls_names[ver_for_check], tls_names[ver_for_check]); /* Check with libcurl set to the next TLS version relative to MHD setting */ if (KNOWN_TLS_MAX == ver_for_check) { printf ("Test is incomplete as the latest known TLS version ('%s') " "was found as minimum working version.\nThere is no space to " "advance to the next version.\nAssuming that test is fine.\n", tls_names[ver_for_check]); fflush (stdout); return 0; } if (CURL_SSLVERSION_LAST == libcurl_tls_vers_map[ver_for_check + 1]) { printf ("Test is incomplete as '%s' is the latest version supported " "by libcurl.\nThere is no space to " "advance to the next version.\nAssuming that test is fine.\n", tls_names[ver_for_check]); fflush (stdout); return 0; } printf ("Starting check with MHD set to '%s' and " "minimum libcurl TLS version set to '%s' " "(failed connection is expected)...\n", tls_names[ver_for_check], tls_names[ver_for_check + 1]); fflush (stdout); res = check_tls_match (ver_for_check, ver_for_check + 1, &port); if (CHECK_RES_HARD_ERROR == res) { fprintf (stderr, "Hard error. Test stopped.\n"); fflush (stderr); return 99; } else if (CHECK_RES_ERR == res) { printf ("Test failed.\n"); fflush (stdout); return 2; } else if (CHECK_RES_MHD_START_FAILED == res) { printf ("MHD cannot be started for the second time with " "the same setting.\n"); fflush (stdout); return 4; } else if (CHECK_RES_CURL_TLS_INIT_FAIL == res) { printf ("'%s' has been rejected by libcurl.\n" "Assuming that test is fine.\n", tls_names[ver_for_check + 1]); fflush (stdout); return 0; } else if (CHECK_RES_CURL_TLS_CONN_FAIL == res) { printf ("As expected, libcurl cannot connect to MHD when libcurl " "minimum TLS version is set to '%s' while MHD TLS version set " "to '%s'.\n" "Test succeeded.\n", tls_names[ver_for_check + 1], tls_names[ver_for_check]); fflush (stdout); return 0; } } fprintf (stderr, "The test skipped: No know TLS versions are supported by " "both MHD and libcurl.\n"); fflush (stderr); return 77; } int main (int argc, char *const *argv) { unsigned int errorCount = 0; const char *ssl_version; (void) argc; /* Unused. Silent compiler warning. */ #ifdef MHD_HTTPS_REQUIRE_GCRYPT gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); #ifdef GCRYCTL_INITIALIZATION_FINISHED gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); #endif #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ if (! testsuite_curl_global_init ()) return 99; ssl_version = curl_version_info (CURLVERSION_NOW)->ssl_version; if (NULL == ssl_version) { fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n"); curl_global_cleanup (); return 77; } errorCount = test_first_supported_versions (); fflush (stderr); fflush (stdout); curl_global_cleanup (); if (77 == errorCount) return 77; else if (99 == errorCount) return 99; print_test_result (errorCount, argv[0]); return errorCount != 0 ? 1 : 0; }