|
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, ©Buffer);
|
|
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
|