test_tls_options.c (14531B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2016 Christian Grothoff 4 Copyright (C) 2014-2022 Evgeny Grin (Karlson2k) 5 6 libmicrohttpd is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published 8 by the Free Software Foundation; either version 2, or (at your 9 option) any later version. 10 11 libmicrohttpd is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with libmicrohttpd; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 Boston, MA 02110-1301, USA. 20 */ 21 22 /** 23 * @file test_tls_options.c 24 * @brief Testcase for libmicrohttpd HTTPS TLS version match/mismatch 25 * @author Sagie Amir 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "platform.h" 30 #include "microhttpd.h" 31 #include <curl/curl.h> 32 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 33 #include <gcrypt.h> 34 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 35 #include "tls_test_common.h" 36 #include "tls_test_keys.h" 37 38 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 39 test into a marked, classifiable test error (TESTING.md, P5). */ 40 #include "mhd_panic_tripwire.h" 41 42 /* 43 * HTTP access handler call back 44 * used to query negotiated security parameters 45 */ 46 static enum MHD_Result 47 simple_ahc (void *cls, struct MHD_Connection *connection, 48 const char *url, const char *method, 49 const char *version, const char *upload_data, 50 size_t *upload_data_size, void **req_cls) 51 { 52 struct MHD_Response *response; 53 enum MHD_Result ret; 54 (void) cls; (void) url; (void) method; (void) version; /* Unused. Silent compiler warning. */ 55 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 56 57 if (NULL == *req_cls) 58 { 59 *req_cls = (void *) &simple_ahc; 60 return MHD_YES; 61 } 62 63 response = 64 MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (EMPTY_PAGE), 65 EMPTY_PAGE); 66 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 67 MHD_destroy_response (response); 68 return ret; 69 } 70 71 72 enum check_result 73 { 74 CHECK_RES_OK = 0, 75 CHECK_RES_ERR = 1, 76 77 CHECK_RES_MHD_START_FAILED = 17, 78 CHECK_RES_CURL_TLS_INIT_FAIL = 18, 79 CHECK_RES_CURL_TLS_CONN_FAIL = 19, 80 81 CHECK_RES_HARD_ERROR = 99 82 }; 83 84 static enum check_result 85 check_tls_match_inner (enum know_gnutls_tls_id tls_ver_mhd, 86 enum know_gnutls_tls_id tls_ver_libcurl, 87 uint16_t *pport, 88 struct MHD_Daemon **d_ptr, 89 struct CBC *pcbc, 90 CURL **c_ptr) 91 { 92 CURLcode errornum; 93 char url[256]; 94 int libcurl_tls_set; 95 CURL *c; 96 struct MHD_Daemon *d; 97 98 /* setup test */ 99 d = 100 MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 101 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS 102 | MHD_USE_ERROR_LOG, *pport, 103 NULL, NULL, 104 &simple_ahc, NULL, 105 MHD_OPTION_HTTPS_PRIORITIES, priorities_map[tls_ver_mhd], 106 MHD_OPTION_HTTPS_MEM_KEY, srv_self_signed_key_pem, 107 MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, 108 MHD_OPTION_END); 109 fflush (stderr); 110 fflush (stdout); 111 *d_ptr = d; 112 113 if (d == NULL) 114 { 115 fprintf (stderr, "MHD_start_daemon() with %s failed.\n", 116 tls_names[tls_ver_mhd]); 117 return CHECK_RES_MHD_START_FAILED; 118 } 119 if (0 == *pport) 120 { 121 const union MHD_DaemonInfo *dinfo; 122 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 123 if ((NULL == dinfo) || (0 == dinfo->port) ) 124 { 125 fprintf (stderr, "MHD_get_daemon_info() failed.\n"); 126 return CHECK_RES_ERR; 127 } 128 *pport = dinfo->port; /* Use the same port for rest of the checks */ 129 } 130 131 if (0 != gen_test_uri (url, 132 sizeof (url), 133 *pport)) 134 { 135 fprintf (stderr, "failed to generate URI.\n"); 136 return CHECK_RES_CURL_TLS_INIT_FAIL; 137 } 138 c = curl_easy_init (); 139 fflush (stderr); 140 fflush (stdout); 141 *c_ptr = c; 142 if (NULL == c) 143 { 144 fprintf (stderr, "curl_easy_init() failed.\n"); 145 return CHECK_RES_HARD_ERROR; 146 } 147 #ifdef _DEBUG 148 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 149 #endif 150 151 if ((CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_URL, url))) || 152 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 153 CURL_HTTP_VERSION_1_1))) || 154 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L))) || 155 (CURLE_OK != 156 (errornum = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L))) || 157 (CURLE_OK != 158 (errornum = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer))) || 159 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_WRITEDATA, 160 pcbc))) || 161 /* TLS options */ 162 /* currently skip any peer authentication */ 163 (CURLE_OK != 164 (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L))) || 165 (CURLE_OK != 166 (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))) || 167 (CURLE_OK != 168 (errornum = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) || 169 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)))) 170 { 171 fflush (stderr); 172 fflush (stdout); 173 fprintf (stderr, "Error setting libcurl option: %s.\n", 174 curl_easy_strerror (errornum)); 175 return CHECK_RES_HARD_ERROR; 176 } 177 libcurl_tls_set = 0; 178 #if CURL_AT_LEAST_VERSION (7,54,0) 179 if (CURL_SSLVERSION_MAX_DEFAULT != 180 libcurl_tls_max_vers_map[tls_ver_libcurl]) 181 { 182 errornum = curl_easy_setopt (c, CURLOPT_SSLVERSION, 183 libcurl_tls_vers_map[tls_ver_libcurl] 184 | libcurl_tls_max_vers_map[tls_ver_libcurl]); 185 if (CURLE_OK == errornum) 186 libcurl_tls_set = 1; 187 else 188 { 189 fprintf (stderr, "Error setting libcurl TLS version range: " 190 "%s.\nRetrying with minimum TLS version only.\n", 191 curl_easy_strerror (errornum)); 192 } 193 } 194 #endif /* CURL_AT_LEAST_VERSION(7,54,0) */ 195 if (! libcurl_tls_set && 196 (CURLE_OK != 197 (errornum = curl_easy_setopt (c, CURLOPT_SSLVERSION, 198 libcurl_tls_vers_map[tls_ver_libcurl])))) 199 { 200 fprintf (stderr, "Error setting libcurl minimum TLS version: %s.\n", 201 curl_easy_strerror (errornum)); 202 return CHECK_RES_CURL_TLS_INIT_FAIL; 203 } 204 205 errornum = curl_easy_perform (c); 206 fflush (stderr); 207 fflush (stdout); 208 if (CURLE_OK != errornum) 209 { 210 if ((CURLE_SSL_CONNECT_ERROR == errornum) || 211 (CURLE_SSL_CIPHER == errornum)) 212 { 213 fprintf (stderr, "libcurl request failed due to TLS error: '%s'\n", 214 curl_easy_strerror (errornum)); 215 return CHECK_RES_CURL_TLS_CONN_FAIL; 216 217 } 218 else 219 { 220 fprintf (stderr, "curl_easy_perform failed: '%s'\n", 221 curl_easy_strerror (errornum)); 222 return CHECK_RES_ERR; 223 } 224 } 225 return CHECK_RES_OK; 226 } 227 228 229 /** 230 * negotiate a secure connection with server with specific TLS versions 231 * set for MHD and for libcurl 232 */ 233 static enum check_result 234 check_tls_match (enum know_gnutls_tls_id tls_ver_mhd, 235 enum know_gnutls_tls_id tls_ver_libcurl, 236 uint16_t *pport) 237 { 238 CURL *c; 239 struct CBC cbc; 240 enum check_result ret; 241 struct MHD_Daemon *d; 242 243 if (NULL == (cbc.buf = malloc (sizeof (char) * 255))) 244 return CHECK_RES_HARD_ERROR; 245 cbc.size = 255; 246 cbc.pos = 0; 247 248 d = NULL; 249 c = NULL; 250 ret = check_tls_match_inner (tls_ver_mhd, tls_ver_libcurl, pport, 251 &d, &cbc, &c); 252 fflush (stderr); 253 fflush (stdout); 254 if (NULL != d) 255 MHD_stop_daemon (d); 256 if (NULL != c) 257 curl_easy_cleanup (c); 258 free (cbc.buf); 259 260 return ret; 261 } 262 263 264 static unsigned int 265 test_first_supported_versions (void) 266 { 267 enum know_gnutls_tls_id ver_for_check; /**< TLS version used for test */ 268 const gnutls_protocol_t *vers_list; /**< The list of GnuTLS supported TLS versions */ 269 uint16_t port; 270 271 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 272 port = 0; /* Use system automatic assignment */ 273 else 274 port = 3080; /* Use predefined port, may break parallel testing of another MHD build */ 275 276 vers_list = gnutls_protocol_list (); 277 if (NULL == vers_list) 278 { 279 fprintf (stderr, "Error getting GnuTLS supported TLS versions"); 280 return 99; 281 } 282 283 for (ver_for_check = KNOWN_TLS_MIN; KNOWN_TLS_MAX >= ver_for_check; 284 ++ver_for_check) 285 { 286 const gnutls_protocol_t *ver_ptr; /**< The pointer to the position on the @a vers_list */ 287 enum check_result res; 288 for (ver_ptr = vers_list; 0 != *ver_ptr; ++ver_ptr) 289 { 290 if (ver_for_check == (enum know_gnutls_tls_id) *ver_ptr) 291 break; 292 } 293 if (0 == *ver_ptr) 294 { 295 printf ("%s is not supported by GnuTLS, skipping.\n\n", 296 tls_names[ver_for_check]); 297 fflush (stdout); 298 continue; 299 } 300 if (CURL_SSLVERSION_LAST == libcurl_tls_vers_map[ver_for_check]) 301 { 302 printf ("%s is not supported by libcurl, skipping.\n\n", 303 tls_names[ver_for_check]); 304 fflush (stdout); 305 continue; 306 } 307 /* Found some TLS version that supported by GnuTLS and should be supported 308 by libcurl (but in practice support depends on used TLS library) */ 309 310 if (KNOWN_TLS_MIN != ver_for_check) 311 printf ("\n"); 312 printf ("Starting check with MHD set to '%s' and " 313 "libcurl set to '%s' (successful connection is expected)...\n", 314 tls_names[ver_for_check], tls_names[ver_for_check]); 315 fflush (stdout); 316 317 /* Check with MHD and libcurl set to the same TLS version */ 318 res = check_tls_match (ver_for_check, ver_for_check, &port); 319 if (CHECK_RES_HARD_ERROR == res) 320 { 321 fprintf (stderr, "Hard error. Test stopped.\n"); 322 fflush (stderr); 323 return 99; 324 } 325 else if (CHECK_RES_ERR == res) 326 { 327 printf ("Test failed.\n"); 328 fflush (stdout); 329 return 2; 330 } 331 else if (CHECK_RES_MHD_START_FAILED == res) 332 { 333 printf ("Skipping '%s' as MHD cannot be started with this setting.\n", 334 tls_names[ver_for_check]); 335 fflush (stdout); 336 continue; 337 } 338 else if (CHECK_RES_CURL_TLS_INIT_FAIL == res) 339 { 340 printf ("Skipping '%s' as libcurl rejected this setting.\n", 341 tls_names[ver_for_check]); 342 fflush (stdout); 343 continue; 344 } 345 else if (CHECK_RES_CURL_TLS_CONN_FAIL == res) 346 { 347 printf ("Skipping '%s' as it is not supported by current libcurl " 348 "and GnuTLS combination.\n", 349 tls_names[ver_for_check]); 350 fflush (stdout); 351 continue; 352 } 353 printf ("Connection succeeded for MHD set to '%s' and " 354 "libcurl set to '%s'.\n\n", 355 tls_names[ver_for_check], tls_names[ver_for_check]); 356 357 /* Check with libcurl set to the next TLS version relative to MHD setting */ 358 if (KNOWN_TLS_MAX == ver_for_check) 359 { 360 printf ("Test is incomplete as the latest known TLS version ('%s') " 361 "was found as minimum working version.\nThere is no space to " 362 "advance to the next version.\nAssuming that test is fine.\n", 363 tls_names[ver_for_check]); 364 fflush (stdout); 365 return 0; 366 } 367 if (CURL_SSLVERSION_LAST == libcurl_tls_vers_map[ver_for_check + 1]) 368 { 369 printf ("Test is incomplete as '%s' is the latest version supported " 370 "by libcurl.\nThere is no space to " 371 "advance to the next version.\nAssuming that test is fine.\n", 372 tls_names[ver_for_check]); 373 fflush (stdout); 374 return 0; 375 } 376 printf ("Starting check with MHD set to '%s' and " 377 "minimum libcurl TLS version set to '%s' " 378 "(failed connection is expected)...\n", 379 tls_names[ver_for_check], tls_names[ver_for_check + 1]); 380 fflush (stdout); 381 res = check_tls_match (ver_for_check, ver_for_check + 1, 382 &port); 383 if (CHECK_RES_HARD_ERROR == res) 384 { 385 fprintf (stderr, "Hard error. Test stopped.\n"); 386 fflush (stderr); 387 return 99; 388 } 389 else if (CHECK_RES_ERR == res) 390 { 391 printf ("Test failed.\n"); 392 fflush (stdout); 393 return 2; 394 } 395 else if (CHECK_RES_MHD_START_FAILED == res) 396 { 397 printf ("MHD cannot be started for the second time with " 398 "the same setting.\n"); 399 fflush (stdout); 400 return 4; 401 } 402 else if (CHECK_RES_CURL_TLS_INIT_FAIL == res) 403 { 404 printf ("'%s' has been rejected by libcurl.\n" 405 "Assuming that test is fine.\n", 406 tls_names[ver_for_check + 1]); 407 fflush (stdout); 408 return 0; 409 } 410 else if (CHECK_RES_CURL_TLS_CONN_FAIL == res) 411 { 412 printf ("As expected, libcurl cannot connect to MHD when libcurl " 413 "minimum TLS version is set to '%s' while MHD TLS version set " 414 "to '%s'.\n" 415 "Test succeeded.\n", 416 tls_names[ver_for_check + 1], tls_names[ver_for_check]); 417 fflush (stdout); 418 return 0; 419 } 420 } 421 422 fprintf (stderr, "The test skipped: No know TLS versions are supported by " 423 "both MHD and libcurl.\n"); 424 fflush (stderr); 425 return 77; 426 } 427 428 429 int 430 main (int argc, char *const *argv) 431 { 432 unsigned int errorCount = 0; 433 const char *ssl_version; 434 (void) argc; /* Unused. Silent compiler warning. */ 435 436 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 437 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 438 #ifdef GCRYCTL_INITIALIZATION_FINISHED 439 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 440 #endif 441 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 442 if (! testsuite_curl_global_init ()) 443 return 99; 444 445 ssl_version = curl_version_info (CURLVERSION_NOW)->ssl_version; 446 if (NULL == ssl_version) 447 { 448 fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n"); 449 curl_global_cleanup (); 450 return 77; 451 } 452 errorCount = test_first_supported_versions (); 453 fflush (stderr); 454 fflush (stdout); 455 curl_global_cleanup (); 456 if (77 == errorCount) 457 return 77; 458 else if (99 == errorCount) 459 return 99; 460 print_test_result (errorCount, argv[0]); 461 return errorCount != 0 ? 1 : 0; 462 }