libmicrohttpd

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

test_https_sni.c (9472B)


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