libmicrohttpd

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

test_https_sni.c (9305B)


      1 /*
      2   This file is part of libmicrohttpd
      3   Copyright (C) 2013, 2016 Christian Grothoff
      4   Copyright (C) 2016-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 3, 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_sni.c
     24  * @brief  Testcase for libmicrohttpd HTTPS with SNI operations
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 #include "platform.h"
     29 #include "microhttpd.h"
     30 #include <limits.h>
     31 #include <sys/stat.h>
     32 #include <curl/curl.h>
     33 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
     34 #include <gcrypt.h>
     35 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
     36 #include "tls_test_common.h"
     37 #include <gnutls/gnutls.h>
     38 
     39 /* This test only works with GnuTLS >= 3.0 */
     40 #if GNUTLS_VERSION_MAJOR >= 3
     41 
     42 #include <gnutls/abstract.h>
     43 
     44 /**
     45  * A hostname, server key and certificate.
     46  */
     47 struct Hosts
     48 {
     49   struct Hosts *next;
     50   const char *hostname;
     51   gnutls_pcert_st pcrt;
     52   gnutls_privkey_t key;
     53 };
     54 
     55 
     56 /**
     57  * Linked list of supported TLDs and respective certificates.
     58  */
     59 static struct Hosts *hosts;
     60 
     61 /* Load the certificate and the private key.
     62  * (This code is largely taken from GnuTLS).
     63  */
     64 static void
     65 load_keys (const char *hostname,
     66            const char *CERT_FILE,
     67            const char *KEY_FILE)
     68 {
     69   int ret;
     70   gnutls_datum_t data;
     71   struct Hosts *host;
     72 
     73   host = malloc (sizeof (struct Hosts));
     74   if (NULL == host)
     75     abort ();
     76   host->hostname = hostname;
     77   host->next = hosts;
     78   hosts = host;
     79 
     80   ret = gnutls_load_file (CERT_FILE, &data);
     81   if (ret < 0)
     82   {
     83     fprintf (stderr,
     84              "*** Error loading certificate file %s.\n",
     85              CERT_FILE);
     86     exit (1);
     87   }
     88   ret =
     89     gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
     90                                   0);
     91   if (ret < 0)
     92   {
     93     fprintf (stderr,
     94              "*** Error loading certificate file: %s\n",
     95              gnutls_strerror (ret));
     96     exit (1);
     97   }
     98   gnutls_free (data.data);
     99 
    100   ret = gnutls_load_file (KEY_FILE, &data);
    101   if (ret < 0)
    102   {
    103     fprintf (stderr,
    104              "*** Error loading key file %s.\n",
    105              KEY_FILE);
    106     exit (1);
    107   }
    108 
    109   gnutls_privkey_init (&host->key);
    110   ret =
    111     gnutls_privkey_import_x509_raw (host->key,
    112                                     &data, GNUTLS_X509_FMT_PEM,
    113                                     NULL, 0);
    114   if (ret < 0)
    115   {
    116     fprintf (stderr,
    117              "*** Error loading key file: %s\n",
    118              gnutls_strerror (ret));
    119     exit (1);
    120   }
    121   gnutls_free (data.data);
    122 }
    123 
    124 
    125 /**
    126  * @param session the session we are giving a cert for
    127  * @param req_ca_dn NULL on server side
    128  * @param nreqs length of req_ca_dn, and thus 0 on server side
    129  * @param pk_algos NULL on server side
    130  * @param pk_algos_length 0 on server side
    131  * @param pcert list of certificates (to be set)
    132  * @param pcert_length length of pcert (to be set)
    133  * @param pkey the private key (to be set)
    134  */
    135 static int
    136 sni_callback (gnutls_session_t session,
    137               const gnutls_datum_t *req_ca_dn,
    138               int nreqs,
    139               const gnutls_pk_algorithm_t *pk_algos,
    140               int pk_algos_length,
    141               gnutls_pcert_st **pcert,
    142               unsigned int *pcert_length,
    143               gnutls_privkey_t *pkey)
    144 {
    145   char name[256];
    146   size_t name_len;
    147   struct Hosts *host;
    148   unsigned int type;
    149   (void) req_ca_dn; (void) nreqs; (void) pk_algos; (void) pk_algos_length;   /* Unused. Silent compiler warning. */
    150 
    151   name_len = sizeof (name);
    152   if (GNUTLS_E_SUCCESS !=
    153       gnutls_server_name_get (session,
    154                               name,
    155                               &name_len,
    156                               &type,
    157                               0 /* index */))
    158     return -1;
    159   for (host = hosts; NULL != host; host = host->next)
    160     if (0 == strncmp (name, host->hostname, name_len))
    161       break;
    162   if (NULL == host)
    163   {
    164     fprintf (stderr,
    165              "Need certificate for %.*s\n",
    166              (int) name_len,
    167              name);
    168     return -1;
    169   }
    170 #if 0
    171   fprintf (stderr,
    172            "Returning certificate for %.*s\n",
    173            (int) name_len,
    174            name);
    175 #endif
    176   *pkey = host->key;
    177   *pcert_length = 1;
    178   *pcert = &host->pcrt;
    179   return 0;
    180 }
    181 
    182 
    183 /* perform a HTTP GET request via SSL/TLS */
    184 static int
    185 do_get (const char *url, uint16_t port)
    186 {
    187   CURL *c;
    188   struct CBC cbc;
    189   CURLcode errornum;
    190   size_t len;
    191   struct curl_slist *dns_info;
    192   char buf[256];
    193 
    194   len = strlen (test_data);
    195   if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
    196   {
    197     fprintf (stderr, MHD_E_MEM);
    198     return -1;
    199   }
    200   cbc.size = len;
    201   cbc.pos = 0;
    202 
    203   c = curl_easy_init ();
    204 #ifdef _DEBUG
    205   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    206 #endif
    207   curl_easy_setopt (c, CURLOPT_URL, url);
    208   curl_easy_setopt (c, CURLOPT_PORT, (long) port);
    209   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    210   curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
    211   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
    212   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    213   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    214   curl_easy_setopt (c, CURLOPT_CAINFO, SRCDIR "/test-ca.crt");
    215 
    216   /* perform peer authentication */
    217   /* TODO merge into send_curl_req */
    218   curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L);
    219   curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 2L);
    220   sprintf (buf, "mhdhost1:%u:127.0.0.1", (unsigned int) port);
    221   dns_info = curl_slist_append (NULL, buf);
    222   sprintf (buf, "mhdhost2:%u:127.0.0.1", (unsigned int) port);
    223   dns_info = curl_slist_append (dns_info, buf);
    224   curl_easy_setopt (c, CURLOPT_RESOLVE, dns_info);
    225   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    226 
    227   /* NOTE: use of CONNECTTIMEOUT without also
    228      setting NOSIGNAL results in really weird
    229      crashes on my system! */
    230   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    231   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    232   {
    233     fprintf (stderr, "curl_easy_perform failed: `%s'\n",
    234              curl_easy_strerror (errornum));
    235     curl_easy_cleanup (c);
    236     free (cbc.buf);
    237     curl_slist_free_all (dns_info);
    238     return -1;
    239   }
    240 
    241   curl_easy_cleanup (c);
    242   curl_slist_free_all (dns_info);
    243   if (memcmp (cbc.buf, test_data, len) != 0)
    244   {
    245     fprintf (stderr, "Error: local file & received file differ.\n");
    246     free (cbc.buf);
    247     return -1;
    248   }
    249 
    250   free (cbc.buf);
    251   return 0;
    252 }
    253 
    254 
    255 int
    256 main (int argc, char *const *argv)
    257 {
    258   unsigned int error_count = 0;
    259   struct MHD_Daemon *d;
    260   uint16_t port;
    261   const char *tls_backend;
    262   (void) argc;   /* Unused. Silent compiler warning. */
    263 
    264   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    265     port = 0;
    266   else
    267     port = 3065;
    268 
    269 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
    270   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    271 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    272   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    273 #endif
    274 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
    275   if (! testsuite_curl_global_init ())
    276     return 99;
    277   tls_backend = curl_version_info (CURLVERSION_NOW)->ssl_version;
    278   if (NULL == tls_backend)
    279   {
    280     fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
    281     curl_global_cleanup ();
    282     return 77;
    283   }
    284   if (! curl_tls_is_gnutls () && ! curl_tls_is_openssl ())
    285   {
    286     fprintf (stderr, "This test is reliable only with libcurl with GnuTLS or "
    287              "OpenSSL backends.\nSkipping the test as libcurl has '%s' "
    288              "backend.\n", tls_backend);
    289     curl_global_cleanup ();
    290     return 77;
    291   }
    292 
    293   load_keys ("mhdhost1", SRCDIR "/mhdhost1.crt",
    294              SRCDIR "/mhdhost1.key");
    295   load_keys ("mhdhost2", SRCDIR "/mhdhost2.crt",
    296              SRCDIR "/mhdhost2.key");
    297   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    298                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS
    299                         | MHD_USE_ERROR_LOG,
    300                         port,
    301                         NULL, NULL,
    302                         &http_ahc, NULL,
    303                         MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
    304                         MHD_OPTION_END);
    305   if (d == NULL)
    306   {
    307     fprintf (stderr, MHD_E_SERVER_INIT);
    308     return -1;
    309   }
    310   if (0 == port)
    311   {
    312     const union MHD_DaemonInfo *dinfo;
    313     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    314     if ((NULL == dinfo) || (0 == dinfo->port) )
    315     {
    316       MHD_stop_daemon (d); return -1;
    317     }
    318     port = dinfo->port;
    319   }
    320   if (0 != do_get ("https://mhdhost1/", port))
    321     error_count++;
    322   if (0 != do_get ("https://mhdhost2/", port))
    323     error_count++;
    324 
    325   MHD_stop_daemon (d);
    326   curl_global_cleanup ();
    327   if (error_count != 0)
    328     fprintf (stderr, "Failed test: %s, error: %u.\n", argv[0], error_count);
    329   return (0 != error_count) ? 1 : 0;
    330 }
    331 
    332 
    333 #else
    334 
    335 int
    336 main (void)
    337 {
    338   fprintf (stderr,
    339            "SNI not supported by GnuTLS < 3.0\n");
    340   return 77;
    341 }
    342 
    343 
    344 #endif