test_https_session_info.c (11087B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2007, 2016 Christian Grothoff 4 Copyright (C) 2016-2021 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_https_session_info.c 24 * @brief Testcase for libmicrohttpd HTTPS connection querying operations 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 static int test_append_prio; 40 41 /* 42 * HTTP access handler call back 43 * used to query negotiated security parameters 44 */ 45 static enum MHD_Result 46 query_info_ahc (void *cls, struct MHD_Connection *connection, 47 const char *url, const char *method, 48 const char *version, const char *upload_data, 49 size_t *upload_data_size, void **req_cls) 50 { 51 struct MHD_Response *response; 52 enum MHD_Result ret; 53 const union MHD_ConnectionInfo *conn_info; 54 enum know_gnutls_tls_id *used_tls_ver; 55 (void) url; (void) method; (void) version; /* Unused. Silent compiler warning. */ 56 (void) upload_data; (void) upload_data_size; /* Unused. Silent compiler warning. */ 57 used_tls_ver = (enum know_gnutls_tls_id *) cls; 58 59 if (NULL == *req_cls) 60 { 61 *req_cls = (void *) &query_info_ahc; 62 return MHD_YES; 63 } 64 65 conn_info = MHD_get_connection_info (connection, 66 MHD_CONNECTION_INFO_PROTOCOL); 67 if (NULL == conn_info) 68 { 69 fflush (stderr); 70 fflush (stdout); 71 fprintf (stderr, "MHD_get_connection_info() failed.\n"); 72 fflush (stderr); 73 return MHD_NO; 74 } 75 if (0 == (unsigned int) conn_info->protocol) 76 { 77 fflush (stderr); 78 fflush (stdout); 79 fprintf (stderr, "MHD_get_connection_info()->protocol has " 80 "wrong zero value.\n"); 81 fflush (stderr); 82 return MHD_NO; 83 } 84 *used_tls_ver = (enum know_gnutls_tls_id) conn_info->protocol; 85 86 response = MHD_create_response_from_buffer_static (strlen (EMPTY_PAGE), 87 EMPTY_PAGE); 88 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 89 MHD_destroy_response (response); 90 return ret; 91 } 92 93 94 /** 95 * negotiate a secure connection with server & query negotiated security parameters 96 */ 97 static unsigned int 98 test_query_session (enum know_gnutls_tls_id tls_ver, uint16_t *pport) 99 { 100 CURL *c; 101 struct CBC cbc; 102 CURLcode errornum; 103 char url[256]; 104 enum know_gnutls_tls_id found_tls_ver; 105 struct MHD_Daemon *d; 106 107 if (NULL == (cbc.buf = malloc (sizeof (char) * 255))) 108 return 99; 109 cbc.size = 255; 110 cbc.pos = 0; 111 112 /* setup test */ 113 found_tls_ver = KNOWN_BAD; 114 d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION 115 | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS 116 | MHD_USE_ERROR_LOG, *pport, 117 NULL, NULL, 118 &query_info_ahc, &found_tls_ver, 119 test_append_prio ? 120 MHD_OPTION_HTTPS_PRIORITIES_APPEND : 121 MHD_OPTION_HTTPS_PRIORITIES, 122 test_append_prio ? 123 priorities_append_map[tls_ver] : 124 priorities_map[tls_ver], 125 MHD_OPTION_HTTPS_MEM_KEY, srv_self_signed_key_pem, 126 MHD_OPTION_HTTPS_MEM_CERT, srv_self_signed_cert_pem, 127 MHD_OPTION_END); 128 129 if (d == NULL) 130 { 131 free (cbc.buf); 132 fprintf (stderr, "MHD_start_daemon() with %s failed.\n", 133 tls_names[tls_ver]); 134 fflush (stderr); 135 return 77; 136 } 137 if (0 == *pport) 138 { 139 const union MHD_DaemonInfo *dinfo; 140 dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT); 141 if ((NULL == dinfo) || (0 == dinfo->port) ) 142 { 143 MHD_stop_daemon (d); 144 free (cbc.buf); 145 fprintf (stderr, "MHD_get_daemon_info() failed.\n"); 146 fflush (stderr); 147 return 10; 148 } 149 *pport = dinfo->port; /* Use the same port for rest of the checks */ 150 } 151 152 gen_test_uri (url, 153 sizeof (url), 154 *pport); 155 c = curl_easy_init (); 156 fflush (stderr); 157 if (NULL == c) 158 { 159 fprintf (stderr, "curl_easy_init() failed.\n"); 160 fflush (stderr); 161 MHD_stop_daemon (d); 162 free (cbc.buf); 163 return 99; 164 } 165 #ifdef _DEBUG 166 curl_easy_setopt (c, CURLOPT_VERBOSE, 1L); 167 #endif 168 169 if ((CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_URL, url))) || 170 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 171 CURL_HTTP_VERSION_1_1))) || 172 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L))) || 173 (CURLE_OK != 174 (errornum = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L))) || 175 (CURLE_OK != 176 (errornum = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer))) || 177 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_WRITEDATA, 178 &cbc))) || 179 /* TLS options */ 180 /* currently skip any peer authentication */ 181 (CURLE_OK != 182 (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L))) || 183 (CURLE_OK != 184 (errornum = curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L))) || 185 (CURLE_OK != 186 (errornum = curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L))) || 187 (CURLE_OK != (errornum = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)))) 188 { 189 curl_easy_cleanup (c); 190 free (cbc.buf); 191 MHD_stop_daemon (d); 192 fflush (stderr); 193 fflush (stdout); 194 fprintf (stderr, "Error setting libcurl option: %s.\n", 195 curl_easy_strerror (errornum)); 196 fflush (stderr); 197 return 99; 198 } 199 200 if (CURLE_OK != (errornum = curl_easy_perform (c))) 201 { 202 unsigned int ret; 203 curl_easy_cleanup (c); 204 free (cbc.buf); 205 MHD_stop_daemon (d); 206 207 fflush (stderr); 208 fflush (stdout); 209 if ((CURLE_SSL_CONNECT_ERROR == errornum) || 210 (CURLE_SSL_CIPHER == errornum)) 211 { 212 ret = 77; 213 fprintf (stderr, "libcurl request failed due to TLS error: '%s'\n", 214 curl_easy_strerror (errornum)); 215 216 } 217 else 218 { 219 ret = 1; 220 fprintf (stderr, "curl_easy_perform failed: '%s'\n", 221 curl_easy_strerror (errornum)); 222 } 223 fflush (stderr); 224 225 return ret; 226 } 227 228 curl_easy_cleanup (c); 229 free (cbc.buf); 230 MHD_stop_daemon (d); 231 232 if (tls_ver != found_tls_ver) 233 { 234 fflush (stderr); 235 fflush (stdout); 236 fprintf (stderr, "MHD_get_connection_info (conn, " 237 "MHD_CONNECTION_INFO_PROTOCOL) returned unexpected " 238 "protocol version.\n" 239 "\tReturned: %s (%u)\tExpected: %s (%u)\n", 240 ((unsigned int) found_tls_ver) > KNOWN_TLS_MAX ? 241 "[wrong value]" : tls_names[found_tls_ver], 242 (unsigned int) found_tls_ver, 243 tls_names[tls_ver], (unsigned int) tls_ver); 244 fflush (stderr); 245 return 2; 246 } 247 return 0; 248 } 249 250 251 static unsigned int 252 test_all_supported_versions (void) 253 { 254 enum know_gnutls_tls_id ver_for_test; /**< TLS version used for test */ 255 const gnutls_protocol_t *vers_list; /**< The list of GnuTLS supported TLS versions */ 256 uint16_t port; 257 unsigned int num_success; /**< Number of tests succeeded */ 258 unsigned int num_failed; /**< Number of tests failed */ 259 260 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 261 port = 0; /* Use system automatic assignment */ 262 else 263 port = 3060; /* Use predefined port, may break parallel testing of another MHD build */ 264 265 vers_list = gnutls_protocol_list (); 266 if (NULL == vers_list) 267 { 268 fprintf (stderr, "Error getting GnuTLS supported TLS versions"); 269 return 99; 270 } 271 num_success = 0; 272 num_failed = 0; 273 274 for (ver_for_test = KNOWN_TLS_MIN; KNOWN_TLS_MAX >= ver_for_test; 275 ++ver_for_test) 276 { 277 const gnutls_protocol_t *ver_ptr; /**< The pointer to the position on the @a vers_list */ 278 unsigned int res; 279 for (ver_ptr = vers_list; 0 != *ver_ptr; ++ver_ptr) 280 { 281 if (ver_for_test == (enum know_gnutls_tls_id) *ver_ptr) 282 break; 283 } 284 if (0 == *ver_ptr) 285 { 286 printf ("%s is not supported by GnuTLS, skipping.\n\n", 287 tls_names[ver_for_test]); 288 fflush (stdout); 289 continue; 290 } 291 printf ("Starting check for %s...\n", 292 tls_names[ver_for_test]); 293 fflush (stdout); 294 res = test_query_session (ver_for_test, &port); 295 fflush (stderr); 296 fflush (stdout); 297 if (99 == res) 298 { 299 fprintf (stderr, "Hard error. Test stopped.\n"); 300 fflush (stderr); 301 return 99; 302 } 303 else if (77 == res) 304 { 305 printf ("%s does not work with libcurl client and GnuTLS " 306 "server combination, skipping.\n", 307 tls_names[ver_for_test]); 308 fflush (stdout); 309 } 310 else if (0 != res) 311 { 312 fprintf (stderr, "Check failed for %s.\n", 313 tls_names[ver_for_test]); 314 fflush (stderr); 315 num_failed++; 316 } 317 else 318 { 319 printf ("Check succeeded for %s.\n", 320 tls_names[ver_for_test]); 321 fflush (stdout); 322 num_success++; 323 } 324 printf ("\n"); 325 fflush (stdout); 326 } 327 328 if (0 == num_failed) 329 { 330 if (0 == num_success) 331 { 332 fprintf (stderr, "No supported TLS version was found.\n"); 333 fflush (stderr); 334 return 77; 335 } 336 return 0; 337 } 338 return num_failed; 339 } 340 341 342 int 343 main (int argc, char *const *argv) 344 { 345 unsigned int errorCount = 0; 346 const char *ssl_version; 347 (void) argc; /* Unused. Silent compiler warning. */ 348 349 #ifdef MHD_HTTPS_REQUIRE_GCRYPT 350 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 351 #ifdef GCRYCTL_INITIALIZATION_FINISHED 352 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 353 #endif 354 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */ 355 test_append_prio = has_in_name (argv[0], "_append"); 356 if (! testsuite_curl_global_init ()) 357 return 99; 358 359 ssl_version = curl_version_info (CURLVERSION_NOW)->ssl_version; 360 if (NULL == ssl_version) 361 { 362 fprintf (stderr, "Curl does not support SSL. Cannot run the test.\n"); 363 curl_global_cleanup (); 364 return 77; 365 } 366 errorCount = test_all_supported_versions (); 367 fflush (stderr); 368 fflush (stdout); 369 curl_global_cleanup (); 370 if (77 == errorCount) 371 return 77; 372 else if (99 == errorCount) 373 return 99; 374 print_test_result (errorCount, argv[0]); 375 return errorCount != 0 ? 1 : 0; 376 }