Blame src/testcurl/https/test_https_sni.c

Packit 875988
/*
Packit 875988
  This file is part of libmicrohttpd
Packit 875988
  Copyright (C) 2013, 2016 Christian Grothoff
Packit 875988
Packit 875988
  libmicrohttpd is free software; you can redistribute it and/or modify
Packit 875988
  it under the terms of the GNU General Public License as published
Packit 875988
  by the Free Software Foundation; either version 3, or (at your
Packit 875988
  option) any later version.
Packit 875988
Packit 875988
  libmicrohttpd is distributed in the hope that it will be useful, but
Packit 875988
  WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 875988
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 875988
  General Public License for more details.
Packit 875988
Packit 875988
  You should have received a copy of the GNU General Public License
Packit 875988
  along with libmicrohttpd; see the file COPYING.  If not, write to the
Packit 875988
  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Packit 875988
  Boston, MA 02110-1301, USA.
Packit 875988
*/
Packit 875988
Packit 875988
/**
Packit 875988
 * @file test_https_sni.c
Packit 875988
 * @brief  Testcase for libmicrohttpd HTTPS with SNI operations
Packit 875988
 * @author Christian Grothoff
Packit 875988
 */
Packit 875988
#include "platform.h"
Packit 875988
#include "microhttpd.h"
Packit 875988
#include <limits.h>
Packit 875988
#include <sys/stat.h>
Packit 875988
#include <curl/curl.h>
Packit 875988
#ifdef MHD_HTTPS_REQUIRE_GRYPT
Packit 875988
#include <gcrypt.h>
Packit 875988
#endif /* MHD_HTTPS_REQUIRE_GRYPT */
Packit 875988
#include "tls_test_common.h"
Packit 875988
#include <gnutls/gnutls.h>
Packit 875988
Packit 875988
/* This test only works with GnuTLS >= 3.0 */
Packit 875988
#if GNUTLS_VERSION_MAJOR >= 3
Packit 875988
Packit 875988
#include <gnutls/abstract.h>
Packit 875988
Packit 875988
/**
Packit 875988
 * A hostname, server key and certificate.
Packit 875988
 */
Packit 875988
struct Hosts
Packit 875988
{
Packit 875988
  struct Hosts *next;
Packit 875988
  const char *hostname;
Packit 875988
  gnutls_pcert_st pcrt;
Packit 875988
  gnutls_privkey_t key;
Packit 875988
};
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * Linked list of supported TLDs and respective certificates.
Packit 875988
 */
Packit 875988
static struct Hosts *hosts;
Packit 875988
Packit 875988
/* Load the certificate and the private key.
Packit 875988
 * (This code is largely taken from GnuTLS).
Packit 875988
 */
Packit 875988
static void
Packit 875988
load_keys(const char *hostname,
Packit 875988
          const char *CERT_FILE,
Packit 875988
          const char *KEY_FILE)
Packit 875988
{
Packit 875988
  int ret;
Packit 875988
  gnutls_datum_t data;
Packit 875988
  struct Hosts *host;
Packit 875988
Packit 875988
  host = malloc (sizeof (struct Hosts));
Packit 875988
  if (NULL == host)
Packit 875988
    abort ();
Packit 875988
  host->hostname = hostname;
Packit 875988
  host->next = hosts;
Packit 875988
  hosts = host;
Packit 875988
Packit 875988
  ret = gnutls_load_file (CERT_FILE, &data);
Packit 875988
  if (ret < 0)
Packit 875988
    {
Packit 875988
      fprintf (stderr,
Packit 875988
               "*** Error loading certificate file %s.\n",
Packit 875988
               CERT_FILE);
Packit 875988
      exit (1);
Packit 875988
    }
Packit 875988
  ret =
Packit 875988
    gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
Packit 875988
                                  0);
Packit 875988
  if (ret < 0)
Packit 875988
    {
Packit 875988
      fprintf (stderr,
Packit 875988
               "*** Error loading certificate file: %s\n",
Packit 875988
               gnutls_strerror (ret));
Packit 875988
      exit (1);
Packit 875988
    }
Packit 875988
  gnutls_free (data.data);
Packit 875988
Packit 875988
  ret = gnutls_load_file (KEY_FILE, &data);
Packit 875988
  if (ret < 0)
Packit 875988
    {
Packit 875988
      fprintf (stderr,
Packit 875988
               "*** Error loading key file %s.\n",
Packit 875988
               KEY_FILE);
Packit 875988
      exit (1);
Packit 875988
    }
Packit 875988
Packit 875988
  gnutls_privkey_init (&host->key);
Packit 875988
  ret =
Packit 875988
    gnutls_privkey_import_x509_raw (host->key,
Packit 875988
                                    &data, GNUTLS_X509_FMT_PEM,
Packit 875988
                                    NULL, 0);
Packit 875988
  if (ret < 0)
Packit 875988
    {
Packit 875988
      fprintf (stderr,
Packit 875988
               "*** Error loading key file: %s\n",
Packit 875988
               gnutls_strerror (ret));
Packit 875988
      exit (1);
Packit 875988
    }
Packit 875988
  gnutls_free (data.data);
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
Packit 875988
/**
Packit 875988
 * @param session the session we are giving a cert for
Packit 875988
 * @param req_ca_dn NULL on server side
Packit 875988
 * @param nreqs length of req_ca_dn, and thus 0 on server side
Packit 875988
 * @param pk_algos NULL on server side
Packit 875988
 * @param pk_algos_length 0 on server side
Packit 875988
 * @param pcert list of certificates (to be set)
Packit 875988
 * @param pcert_length length of pcert (to be set)
Packit 875988
 * @param pkey the private key (to be set)
Packit 875988
 */
Packit 875988
static int
Packit 875988
sni_callback (gnutls_session_t session,
Packit 875988
              const gnutls_datum_t* req_ca_dn,
Packit 875988
              int nreqs,
Packit 875988
              const gnutls_pk_algorithm_t* pk_algos,
Packit 875988
              int pk_algos_length,
Packit 875988
              gnutls_pcert_st** pcert,
Packit 875988
              unsigned int *pcert_length,
Packit 875988
              gnutls_privkey_t * pkey)
Packit 875988
{
Packit 875988
  char name[256];
Packit 875988
  size_t name_len;
Packit 875988
  struct Hosts *host;
Packit 875988
  unsigned int type;
Packit 875988
  (void)req_ca_dn;(void)nreqs;(void)pk_algos;(void)pk_algos_length;   /* Unused. Silent compiler warning. */
Packit 875988
Packit 875988
  name_len = sizeof (name);
Packit 875988
  if (GNUTLS_E_SUCCESS !=
Packit 875988
      gnutls_server_name_get (session,
Packit 875988
                              name,
Packit 875988
                              &name_len,
Packit 875988
                              &type,
Packit 875988
                              0 /* index */))
Packit 875988
    return -1;
Packit 875988
  for (host = hosts; NULL != host; host = host->next)
Packit 875988
    if (0 == strncmp (name, host->hostname, name_len))
Packit 875988
      break;
Packit 875988
  if (NULL == host)
Packit 875988
    {
Packit 875988
      fprintf (stderr,
Packit 875988
               "Need certificate for %.*s\n",
Packit 875988
               (int) name_len,
Packit 875988
               name);
Packit 875988
      return -1;
Packit 875988
    }
Packit 875988
#if 0
Packit 875988
  fprintf (stderr,
Packit 875988
           "Returning certificate for %.*s\n",
Packit 875988
           (int) name_len,
Packit 875988
           name);
Packit 875988
#endif
Packit 875988
  *pkey = host->key;
Packit 875988
  *pcert_length = 1;
Packit 875988
  *pcert = &host->pcrt;
Packit 875988
  return 0;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
/* perform a HTTP GET request via SSL/TLS */
Packit 875988
static int
Packit 875988
do_get (const char *url, int port)
Packit 875988
{
Packit 875988
  CURL *c;
Packit 875988
  struct CBC cbc;
Packit 875988
  CURLcode errornum;
Packit 875988
  size_t len;
Packit 875988
  struct curl_slist *dns_info;
Packit 875988
  char buf[256];
Packit 875988
Packit 875988
  len = strlen (test_data);
Packit 875988
  if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
Packit 875988
    {
Packit 875988
      fprintf (stderr, MHD_E_MEM);
Packit 875988
      return -1;
Packit 875988
    }
Packit 875988
  cbc.size = len;
Packit 875988
  cbc.pos = 0;
Packit 875988
Packit 875988
  c = curl_easy_init ();
Packit 875988
#if DEBUG_HTTPS_TEST
Packit 875988
  curl_easy_setopt (c, CURLOPT_VERBOSE, 1);
Packit 875988
#endif
Packit 875988
  curl_easy_setopt (c, CURLOPT_URL, url);
Packit 875988
  curl_easy_setopt (c, CURLOPT_PORT, (long)port);
Packit 875988
  curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
Packit 875988
  curl_easy_setopt (c, CURLOPT_TIMEOUT, 10L);
Packit 875988
  curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 10L);
Packit 875988
  curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
Packit 875988
  curl_easy_setopt (c, CURLOPT_FILE, &cbc);
Packit 875988
Packit 875988
  /* perform peer authentication */
Packit 875988
  /* TODO merge into send_curl_req */
Packit 875988
  curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0);
Packit 875988
  curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 2);
Packit 875988
  sprintf(buf, "host1:%d:127.0.0.1", port);
Packit 875988
  dns_info = curl_slist_append (NULL, buf);
Packit 875988
  sprintf(buf, "host2:%d:127.0.0.1", port);
Packit 875988
  dns_info = curl_slist_append (dns_info, buf);
Packit 875988
  curl_easy_setopt (c, CURLOPT_RESOLVE, dns_info);
Packit 875988
  curl_easy_setopt (c, CURLOPT_FAILONERROR, 1);
Packit 875988
Packit 875988
  /* NOTE: use of CONNECTTIMEOUT without also
Packit 875988
     setting NOSIGNAL results in really weird
Packit 875988
     crashes on my system! */
Packit 875988
  curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1);
Packit 875988
  if (CURLE_OK != (errornum = curl_easy_perform (c)))
Packit 875988
    {
Packit 875988
      fprintf (stderr, "curl_easy_perform failed: `%s'\n",
Packit 875988
               curl_easy_strerror (errornum));
Packit 875988
      curl_easy_cleanup (c);
Packit 875988
      free (cbc.buf);
Packit 875988
      curl_slist_free_all (dns_info);
Packit 875988
      return errornum;
Packit 875988
    }
Packit 875988
Packit 875988
  curl_easy_cleanup (c);
Packit 875988
  curl_slist_free_all (dns_info);
Packit 875988
  if (memcmp (cbc.buf, test_data, len) != 0)
Packit 875988
    {
Packit 875988
      fprintf (stderr, "Error: local file & received file differ.\n");
Packit 875988
      free (cbc.buf);
Packit 875988
      return -1;
Packit 875988
    }
Packit 875988
Packit 875988
  free (cbc.buf);
Packit 875988
  return 0;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
int
Packit 875988
main (int argc, char *const *argv)
Packit 875988
{
Packit 875988
  unsigned int error_count = 0;
Packit 875988
  struct MHD_Daemon *d;
Packit 875988
  int port;
Packit 875988
  (void)argc;   /* Unused. Silent compiler warning. */
Packit 875988
Packit 875988
  if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
Packit 875988
    port = 0;
Packit 875988
  else
Packit 875988
    port = 3060;
Packit 875988
Packit 875988
#ifdef MHD_HTTPS_REQUIRE_GRYPT
Packit 875988
  gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
Packit 875988
#ifdef GCRYCTL_INITIALIZATION_FINISHED
Packit 875988
  gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
Packit 875988
#endif
Packit 875988
#endif /* MHD_HTTPS_REQUIRE_GRYPT */
Packit 875988
  if (!testsuite_curl_global_init ())
Packit 875988
    return 99;
Packit 875988
  if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version)
Packit 875988
    {
Packit 875988
      fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
Packit 875988
      curl_global_cleanup ();
Packit 875988
      return 77;
Packit 875988
    }
Packit 875988
Packit 875988
  load_keys ("host1", ABS_SRCDIR "/host1.crt", ABS_SRCDIR "/host1.key");
Packit 875988
  load_keys ("host2", ABS_SRCDIR "/host2.crt", ABS_SRCDIR "/host2.key");
Packit 875988
  d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS | MHD_USE_ERROR_LOG,
Packit 875988
                        port,
Packit 875988
                        NULL, NULL,
Packit 875988
                        &http_ahc, NULL,
Packit 875988
                        MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
Packit 875988
                        MHD_OPTION_END);
Packit 875988
  if (d == NULL)
Packit 875988
    {
Packit 875988
      fprintf (stderr, MHD_E_SERVER_INIT);
Packit 875988
      return -1;
Packit 875988
    }
Packit 875988
  if (0 == port)
Packit 875988
    {
Packit 875988
      const union MHD_DaemonInfo *dinfo;
Packit 875988
      dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
Packit 875988
      if (NULL == dinfo || 0 == dinfo->port)
Packit 875988
        { MHD_stop_daemon (d); return -1; }
Packit 875988
      port = (int)dinfo->port;
Packit 875988
    }
Packit 875988
  if (0 != do_get ("https://host1/", port))
Packit 875988
    error_count++;
Packit 875988
  if (0 != do_get ("https://host2/", port))
Packit 875988
    error_count++;
Packit 875988
Packit 875988
  MHD_stop_daemon (d);
Packit 875988
  curl_global_cleanup ();
Packit 875988
  if (error_count != 0)
Packit 875988
    fprintf (stderr, "Failed test: %s, error: %u.\n", argv[0], error_count);
Packit 875988
  return (0 != error_count) ? 1 : 0;
Packit 875988
}
Packit 875988
Packit 875988
Packit 875988
#else
Packit 875988
Packit 875988
int main (void)
Packit 875988
{
Packit 875988
  fprintf (stderr,
Packit 875988
           "SNI not supported by GnuTLS < 3.0\n");
Packit 875988
  return 77;
Packit 875988
}
Packit 875988
#endif