libmicrohttpd

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

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