libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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, &copyBuffer))) ||
    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 }