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