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