mirror of
https://git.gnunet.org/libmicrohttpd.git
synced 2026-09-25 04:09:31 +03:00
add support for SNI
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
Sat Dec 21 17:26:08 CET 2013
|
||||
Fixed an issue with a missing argument in the postexample.
|
||||
Fixed issue with bogus offset increment involving sendfile
|
||||
on GNU/Linux.
|
||||
on GNU/Linux. Adding support for SNI. -CG
|
||||
|
||||
Mon Dec 9 21:41:57 CET 2013
|
||||
Fix for per-worker daemon pipes enabled with
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
We left the basic authentication chapter with the unsatisfactory conclusion that
|
||||
any traffic, including the credentials, could be intercepted by anyone between
|
||||
the browser client and the server. Protecting the data while it is sent over
|
||||
the browser client and the server. Protecting the data while it is sent over
|
||||
unsecured lines will be the goal of this chapter.
|
||||
|
||||
Since version 0.4, the @emph{MHD} library includes support for encrypting the
|
||||
@@ -23,7 +23,7 @@ this tutorial, we will be content with a 1024 bit key:
|
||||
|
||||
In addition to the key, a certificate describing the server in human readable tokens
|
||||
is also needed. This certificate will be attested with our aforementioned key. In this way,
|
||||
we obtain a self-signed certificate, valid for one year.
|
||||
we obtain a self-signed certificate, valid for one year.
|
||||
|
||||
@verbatim
|
||||
> openssl req -days 365 -out server.pem -new -x509 -key server.key
|
||||
@@ -38,7 +38,7 @@ any visitor can make sure the server's identity is real.
|
||||
|
||||
Whether the server's certificate is signed by us or a third party, once it has been accepted
|
||||
by the client, both sides will be communicating over encrypted channels. From this point on,
|
||||
it is the client's turn to authenticate itself. But this has already been implemented in the basic
|
||||
it is the client's turn to authenticate itself. But this has already been implemented in the basic
|
||||
authentication scheme.
|
||||
|
||||
|
||||
@@ -65,12 +65,12 @@ main ()
|
||||
@end verbatim
|
||||
@noindent
|
||||
|
||||
and then we point the @emph{MHD} daemon to it upon initalization.
|
||||
and then we point the @emph{MHD} daemon to it upon initalization.
|
||||
@verbatim
|
||||
|
||||
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
|
||||
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
|
||||
PORT, NULL, NULL,
|
||||
&answer_to_connection, NULL,
|
||||
&answer_to_connection, NULL,
|
||||
MHD_OPTION_HTTPS_MEM_KEY, key_pem,
|
||||
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
|
||||
MHD_OPTION_END);
|
||||
@@ -78,10 +78,10 @@ and then we point the @emph{MHD} daemon to it upon initalization.
|
||||
if (NULL == daemon)
|
||||
{
|
||||
printf ("%s\n", cert_pem);
|
||||
|
||||
|
||||
free (key_pem);
|
||||
free (cert_pem);
|
||||
|
||||
|
||||
return 1;
|
||||
}
|
||||
@end verbatim
|
||||
@@ -96,7 +96,7 @@ The rest consists of little new besides some additional memory cleanups.
|
||||
MHD_stop_daemon (daemon);
|
||||
free (key_pem);
|
||||
free (cert_pem);
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
@end verbatim
|
||||
@@ -110,18 +110,18 @@ The rather unexciting file loader can be found in the complete example @code{tls
|
||||
@itemize @bullet
|
||||
@item
|
||||
While the standard @emph{HTTP} port is 80, it is 443 for @emph{HTTPS}. The common internet browsers assume
|
||||
standard @emph{HTTP} if they are asked to access other ports than these. Therefore, you will have to type
|
||||
standard @emph{HTTP} if they are asked to access other ports than these. Therefore, you will have to type
|
||||
@code{https://localhost:8888} explicitly when you test the example, or the browser will not know how to
|
||||
handle the answer properly.
|
||||
|
||||
@item
|
||||
The remaining weak point is the question how the server will be trusted initially. Either a @emph{CA} signs the
|
||||
certificate or the client obtains the key over secure means. Anyway, the clients have to be aware (or configured)
|
||||
certificate or the client obtains the key over secure means. Anyway, the clients have to be aware (or configured)
|
||||
that they should not accept certificates of unknown origin.
|
||||
|
||||
@item
|
||||
The introduced method of certificates makes it mandatory to set an expiration date---making it less feasible to
|
||||
hardcode certificates in embedded devices.
|
||||
hardcode certificates in embedded devices.
|
||||
|
||||
@item
|
||||
The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists
|
||||
@@ -135,12 +135,12 @@ both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}.
|
||||
You can also use MHD to authenticate the client via SSL/TLS certificates
|
||||
(as an alternative to using the password-based Basic or Digest authentication).
|
||||
To do this, you will need to link your application against @emph{gnutls}.
|
||||
Next, when you start the MHD daemon, you must specify the root CA that you're
|
||||
Next, when you start the MHD daemon, you must specify the root CA that you're
|
||||
willing to trust:
|
||||
@verbatim
|
||||
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
|
||||
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
|
||||
PORT, NULL, NULL,
|
||||
&answer_to_connection, NULL,
|
||||
&answer_to_connection, NULL,
|
||||
MHD_OPTION_HTTPS_MEM_KEY, key_pem,
|
||||
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
|
||||
MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem,
|
||||
@@ -159,7 +159,7 @@ obtain the raw GnuTLS session handle from @emph{MHD} using
|
||||
gnutls_session_t tls_session;
|
||||
union MHD_ConnectionInfo *ci;
|
||||
|
||||
ci = MHD_get_connection_info (connection,
|
||||
ci = MHD_get_connection_info (connection,
|
||||
MHD_CONNECTION_INFO_GNUTLS_SESSION);
|
||||
tls_session = ci->tls_session;
|
||||
@end verbatim
|
||||
@@ -172,31 +172,31 @@ You can then extract the client certificate:
|
||||
*
|
||||
* @param tls_session the TLS session
|
||||
* @return NULL if no valid client certificate could be found, a pointer
|
||||
* to the certificate if found
|
||||
* to the certificate if found
|
||||
*/
|
||||
static gnutls_x509_crt_t
|
||||
get_client_certificate (gnutls_session_t tls_session)
|
||||
get_client_certificate (gnutls_session_t tls_session)
|
||||
{
|
||||
unsigned int listsize;
|
||||
const gnutls_datum_t * pcert;
|
||||
gnutls_certificate_status_t client_cert_status;
|
||||
gnutls_x509_crt_t client_cert;
|
||||
|
||||
if (tls_session == NULL)
|
||||
if (tls_session == NULL)
|
||||
return NULL;
|
||||
if (gnutls_certificate_verify_peers2(tls_session,
|
||||
&client_cert_status))
|
||||
&client_cert_status))
|
||||
return NULL;
|
||||
pcert = gnutls_certificate_get_peers(tls_session,
|
||||
pcert = gnutls_certificate_get_peers(tls_session,
|
||||
&listsize);
|
||||
if ( (pcert == NULL) ||
|
||||
(listsize == 0))
|
||||
if ( (pcert == NULL) ||
|
||||
(listsize == 0))
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to retrieve client certificate chain\n");
|
||||
return NULL;
|
||||
}
|
||||
if (gnutls_x509_crt_init(&client_cert))
|
||||
}
|
||||
if (gnutls_x509_crt_init(&client_cert))
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to initialize client certificate\n");
|
||||
@@ -204,15 +204,15 @@ get_client_certificate (gnutls_session_t tls_session)
|
||||
}
|
||||
/* Note that by passing values between 0 and listsize here, you
|
||||
can get access to the CA's certs */
|
||||
if (gnutls_x509_crt_import(client_cert,
|
||||
if (gnutls_x509_crt_import(client_cert,
|
||||
&pcert[0],
|
||||
GNUTLS_X509_FMT_DER))
|
||||
GNUTLS_X509_FMT_DER))
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to import client certificate\n");
|
||||
gnutls_x509_crt_deinit(client_cert);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return client_cert;
|
||||
}
|
||||
@end verbatim
|
||||
@@ -229,15 +229,15 @@ and alternative names:
|
||||
* to the dn if found
|
||||
*/
|
||||
char *
|
||||
cert_auth_get_dn(gnutls_x509_crt_c client_cert)
|
||||
cert_auth_get_dn(gnutls_x509_crt_c client_cert)
|
||||
{
|
||||
char* buf;
|
||||
size_t lbuf;
|
||||
size_t lbuf;
|
||||
|
||||
lbuf = 0;
|
||||
gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf);
|
||||
buf = malloc(lbuf);
|
||||
if (buf == NULL)
|
||||
if (buf == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to allocate memory for certificate dn\n");
|
||||
@@ -260,8 +260,8 @@ cert_auth_get_dn(gnutls_x509_crt_c client_cert)
|
||||
*/
|
||||
char *
|
||||
MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
|
||||
int nametype,
|
||||
unsigned int index)
|
||||
int nametype,
|
||||
unsigned int index)
|
||||
{
|
||||
char* buf;
|
||||
size_t lbuf;
|
||||
@@ -271,7 +271,7 @@ MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
|
||||
int result;
|
||||
|
||||
subseq = 0;
|
||||
for (seq=0;;seq++)
|
||||
for (seq=0;;seq++)
|
||||
{
|
||||
lbuf = 0;
|
||||
result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf,
|
||||
@@ -280,21 +280,21 @@ MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
|
||||
return NULL;
|
||||
if (nametype != (int) type)
|
||||
continue;
|
||||
if (subseq == index)
|
||||
if (subseq == index)
|
||||
break;
|
||||
subseq++;
|
||||
}
|
||||
buf = malloc(lbuf);
|
||||
if (buf == NULL)
|
||||
if (buf == NULL)
|
||||
{
|
||||
fprintf (stderr,
|
||||
"Failed to allocate memory for certificate alt name\n");
|
||||
return NULL;
|
||||
}
|
||||
result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
|
||||
result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
|
||||
seq,
|
||||
buf,
|
||||
&lbuf,
|
||||
&lbuf,
|
||||
NULL, NULL);
|
||||
if (result != nametype)
|
||||
{
|
||||
@@ -315,3 +315,174 @@ certificate:
|
||||
gnutls_x509_crt_deinit (client_cert);
|
||||
@end verbatim
|
||||
|
||||
|
||||
|
||||
@heading Using TLS Server Name Indication (SNI)
|
||||
|
||||
SNI enables hosting multiple domains under one IP address with TLS. So
|
||||
SNI is the TLS-equivalent of virtual hosting. To use SNI with MHD, you
|
||||
need at least GnuTLS 3.0. The main change compared to the simple hosting
|
||||
of one domain is that you need to provide a callback instead of the key
|
||||
and certificate. For example, when you start the MHD daemon, you could
|
||||
do this:
|
||||
@verbatim
|
||||
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
|
||||
PORT, NULL, NULL,
|
||||
&answer_to_connection, NULL,
|
||||
MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
|
||||
MHD_OPTION_END);
|
||||
@end verbatim
|
||||
Here, @code{sni_callback} is the name of a function that you will have to
|
||||
implement to retrieve the X.509 certificate for an incoming connection.
|
||||
The callback has type @code{gnutls_certificate_retrieve_function2} and
|
||||
is documented in the GnuTLS API for the @code{gnutls_certificate_set_retrieve_function2}
|
||||
as follows:
|
||||
|
||||
@deftypefn {Function Pointer} int {*gnutls_certificate_retrieve_function2} (gnutls_session_t, 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)
|
||||
|
||||
@table @var
|
||||
@item req_ca_cert
|
||||
is only used in X.509 certificates. Contains a list with the CA names that the server considers trusted. Normally we should send a certificate that is signed by one of these CAs. These names are DER encoded. To get a more meaningful value use the function @code{gnutls_x509_rdn_get()}.
|
||||
|
||||
@item pk_algos
|
||||
contains a list with server’s acceptable signature algorithms. The certificate returned should support the server’s given algorithms.
|
||||
|
||||
@item pcert
|
||||
should contain a single certificate and public or a list of them.
|
||||
|
||||
@item pcert_length
|
||||
is the size of the previous list.
|
||||
|
||||
@item pkey
|
||||
is the private key.
|
||||
@end table
|
||||
@end deftypefn
|
||||
|
||||
A possible implementation of this callback would look like this:
|
||||
|
||||
@verbatim
|
||||
struct Hosts
|
||||
{
|
||||
struct Hosts *next;
|
||||
const char *hostname;
|
||||
gnutls_pcert_st pcrt;
|
||||
gnutls_privkey_t key;
|
||||
};
|
||||
|
||||
static struct Hosts *hosts;
|
||||
|
||||
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;
|
||||
}
|
||||
fprintf (stderr,
|
||||
"Returning certificate for %.*s\n",
|
||||
(int) name_len,
|
||||
name);
|
||||
*pkey = host->key;
|
||||
*pcert_length = 1;
|
||||
*pcert = &host->pcrt;
|
||||
return 0;
|
||||
}
|
||||
@end verbatim
|
||||
|
||||
Note that MHD cannot offer passing a closure or any other additional information
|
||||
to this callback, as the GnuTLS API unfortunately does not permit this at this
|
||||
point.
|
||||
|
||||
The @code{hosts} list can be initialized by loading the private keys and X.509
|
||||
certificats from disk as follows:
|
||||
|
||||
@verbatim
|
||||
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));
|
||||
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);
|
||||
}
|
||||
@end verbatim
|
||||
|
||||
The code above was largely lifted from GnuTLS. You can find other
|
||||
methods for initializing certificates and keys in the GnuTLS manual
|
||||
and source code.
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
\input texinfo @c -*-texinfo-*-
|
||||
@finalout
|
||||
@setfilename libmicrohttpd-tutorial.info
|
||||
@set UPDATED 17 July 2012
|
||||
@set UPDATED-MONTH July 2012
|
||||
@set EDITION 0.9.22
|
||||
@set VERSION 0.9.22
|
||||
@set UPDATED 17 November 2013
|
||||
@set UPDATED-MONTH November 2013
|
||||
@set EDITION 0.9.23
|
||||
@set VERSION 0.9.23
|
||||
@settitle A tutorial for GNU libmicrohttpd
|
||||
@c Unify all the indices into concept index.
|
||||
@syncodeindex fn cp
|
||||
@@ -20,11 +20,11 @@
|
||||
|
||||
@copying
|
||||
This tutorial documents GNU libmicrohttpd version @value{VERSION}, last
|
||||
updated @value{UPDATED}.
|
||||
updated @value{UPDATED}.
|
||||
|
||||
Copyright (c) 2008 Sebastian Gerhardt.
|
||||
|
||||
Copyright (c) 2010, 2011, 2012 Christian Grothoff.
|
||||
Copyright (c) 2010, 2011, 2012, 2013 Christian Grothoff.
|
||||
@quotation
|
||||
Permission is granted to copy, distribute and/or modify this document
|
||||
under the terms of the GNU Free Documentation License, Version 1.3
|
||||
@@ -56,7 +56,7 @@ Free Documentation License".
|
||||
@node Top
|
||||
@top A Tutorial for GNU libmicrohttpd
|
||||
@insertcopying
|
||||
@end ifnottex
|
||||
@end ifnottex
|
||||
|
||||
@menu
|
||||
* Introduction::
|
||||
@@ -97,8 +97,8 @@ Free Documentation License".
|
||||
@chapter Processing POST data
|
||||
@include chapters/processingpost.inc
|
||||
|
||||
@node Improved processing of POST data
|
||||
@chapter Improved processing of POST data
|
||||
@node Improved processing of POST data
|
||||
@chapter Improved processing of POST data
|
||||
@include chapters/largerpost.inc
|
||||
|
||||
@node Session management
|
||||
|
||||
@@ -701,6 +701,22 @@ are acceptable for the application. The string is passed
|
||||
unchanged to gnutls_priority_init. If this option is not
|
||||
specified, ``NORMAL'' is used.
|
||||
|
||||
@item MHD_OPTION_HTTPS_CERT_CALLBACK
|
||||
@cindex SSL
|
||||
@cindex TLS
|
||||
@cindex SNI
|
||||
Use a callback to determine which X.509 certificate should be used for
|
||||
a given HTTPS connection. This option should be followed by a
|
||||
argument of type "gnutls_certificate_retrieve_function2 *". This
|
||||
option provides an alternative to MHD_OPTION_HTTPS_MEM_KEY and
|
||||
MHD_OPTION_HTTPS_MEM_CERT. You must use this version if multiple
|
||||
domains are to be hosted at the same IP address using TLS's Server
|
||||
Name Indication (SNI) extension. In this case, the callback is
|
||||
expected to select the correct certificate based on the SNI
|
||||
information provided. The callback is expected to access the SNI data
|
||||
using gnutls_server_name_get(). Using this option requires GnuTLS 3.0
|
||||
or higher.
|
||||
|
||||
@item MHD_OPTION_DIGEST_AUTH_RANDOM
|
||||
@cindex digest auth
|
||||
@cindex random
|
||||
|
||||
+53
-53
@@ -21,7 +21,7 @@
|
||||
* @file demo.c
|
||||
* @brief complex demonstration site: create directory index, offer
|
||||
* upload via form and HTTP POST, download with mime type detection
|
||||
* and error reporting (403, etc.) --- and all of this with
|
||||
* and error reporting (403, etc.) --- and all of this with
|
||||
* high-performance settings (large buffers, thread pool).
|
||||
* If you want to benchmark MHD, this code should be used to
|
||||
* run tests against. Note that the number of threads may need
|
||||
@@ -222,7 +222,7 @@ struct ResponseDataContext
|
||||
* Response data string.
|
||||
*/
|
||||
char *buf;
|
||||
|
||||
|
||||
/**
|
||||
* Number of bytes allocated for 'buf'.
|
||||
*/
|
||||
@@ -253,12 +253,12 @@ list_directory (struct ResponseDataContext *rdc,
|
||||
struct dirent *de;
|
||||
|
||||
if (NULL == (dir = opendir (dirname)))
|
||||
return MHD_NO;
|
||||
return MHD_NO;
|
||||
while (NULL != (de = readdir (dir)))
|
||||
{
|
||||
if ('.' == de->d_name[0])
|
||||
continue;
|
||||
if (sizeof (fullname) <=
|
||||
if (sizeof (fullname) <=
|
||||
snprintf (fullname, sizeof (fullname),
|
||||
"%s/%s",
|
||||
dirname, de->d_name))
|
||||
@@ -278,7 +278,7 @@ list_directory (struct ResponseDataContext *rdc,
|
||||
break; /* out of memory */
|
||||
rdc->buf = r;
|
||||
}
|
||||
rdc->off += snprintf (&rdc->buf[rdc->off],
|
||||
rdc->off += snprintf (&rdc->buf[rdc->off],
|
||||
rdc->buf_len - rdc->off,
|
||||
"<li><a href=\"/%s\">%s</a></li>\n",
|
||||
fullname,
|
||||
@@ -305,11 +305,11 @@ update_directory ()
|
||||
char dir_name[128];
|
||||
struct stat sbuf;
|
||||
|
||||
rdc.buf_len = initial_allocation;
|
||||
rdc.buf_len = initial_allocation;
|
||||
if (NULL == (rdc.buf = malloc (rdc.buf_len)))
|
||||
{
|
||||
update_cached_response (NULL);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
rdc.off = snprintf (rdc.buf, rdc.buf_len,
|
||||
"%s",
|
||||
@@ -342,7 +342,7 @@ update_directory ()
|
||||
rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
|
||||
"<h3>%s</h3>\n",
|
||||
category);
|
||||
|
||||
|
||||
if (MHD_NO == list_directory (&rdc, dir_name))
|
||||
{
|
||||
free (rdc.buf);
|
||||
@@ -352,7 +352,7 @@ update_directory ()
|
||||
}
|
||||
}
|
||||
/* we ensured always +1k room, filenames are ~256 bytes,
|
||||
so there is always still enough space for the footer
|
||||
so there is always still enough space for the footer
|
||||
without need for a final reallocation check. */
|
||||
rdc.off += snprintf (&rdc.buf[rdc.off], rdc.buf_len - rdc.off,
|
||||
"%s",
|
||||
@@ -427,7 +427,7 @@ do_append (char **ret,
|
||||
{
|
||||
char *buf;
|
||||
size_t old_len;
|
||||
|
||||
|
||||
if (NULL == *ret)
|
||||
old_len = 0;
|
||||
else
|
||||
@@ -471,8 +471,8 @@ process_upload_data (void *cls,
|
||||
const char *filename,
|
||||
const char *content_type,
|
||||
const char *transfer_encoding,
|
||||
const char *data,
|
||||
uint64_t off,
|
||||
const char *data,
|
||||
uint64_t off,
|
||||
size_t size)
|
||||
{
|
||||
struct UploadContext *uc = cls;
|
||||
@@ -484,10 +484,10 @@ process_upload_data (void *cls,
|
||||
return do_append (&uc->language, data, size);
|
||||
if (0 != strcmp (key, "upload"))
|
||||
{
|
||||
fprintf (stderr,
|
||||
fprintf (stderr,
|
||||
"Ignoring unexpected form value `%s'\n",
|
||||
key);
|
||||
return MHD_YES; /* ignore */
|
||||
return MHD_YES; /* ignore */
|
||||
}
|
||||
if (NULL == filename)
|
||||
{
|
||||
@@ -497,7 +497,7 @@ process_upload_data (void *cls,
|
||||
if ( (NULL == uc->category) ||
|
||||
(NULL == uc->language) )
|
||||
{
|
||||
fprintf (stderr,
|
||||
fprintf (stderr,
|
||||
"Missing form data for upload `%s'\n",
|
||||
filename);
|
||||
uc->response = request_refused_response;
|
||||
@@ -523,8 +523,8 @@ process_upload_data (void *cls,
|
||||
snprintf (fn, sizeof (fn),
|
||||
"%s/%s",
|
||||
uc->language,
|
||||
uc->category);
|
||||
#ifdef WINDOWS
|
||||
uc->category);
|
||||
#ifdef WINDOWS
|
||||
(void) mkdir (fn);
|
||||
#else
|
||||
(void) mkdir (fn, S_IRWXU);
|
||||
@@ -534,12 +534,12 @@ process_upload_data (void *cls,
|
||||
"%s/%s/%s",
|
||||
uc->language,
|
||||
uc->category,
|
||||
filename);
|
||||
filename);
|
||||
for (i=strlen (fn)-1;i>=0;i--)
|
||||
if (! isprint ((int) fn[i]))
|
||||
fn[i] = '_';
|
||||
uc->fd = open (fn,
|
||||
O_CREAT | O_EXCL
|
||||
uc->fd = open (fn,
|
||||
O_CREAT | O_EXCL
|
||||
#if O_LARGEFILE
|
||||
| O_LARGEFILE
|
||||
#endif
|
||||
@@ -547,20 +547,20 @@ process_upload_data (void *cls,
|
||||
S_IRUSR | S_IWUSR);
|
||||
if (-1 == uc->fd)
|
||||
{
|
||||
fprintf (stderr,
|
||||
fprintf (stderr,
|
||||
"Error opening file `%s' for upload: %s\n",
|
||||
fn,
|
||||
strerror (errno));
|
||||
uc->response = request_refused_response;
|
||||
return MHD_NO;
|
||||
}
|
||||
}
|
||||
uc->filename = strdup (fn);
|
||||
}
|
||||
if ( (0 != size) &&
|
||||
(size != write (uc->fd, data, size)) )
|
||||
(size != write (uc->fd, data, size)) )
|
||||
{
|
||||
/* write failed; likely: disk full */
|
||||
fprintf (stderr,
|
||||
fprintf (stderr,
|
||||
"Error writing to file `%s': %s\n",
|
||||
uc->filename,
|
||||
strerror (errno));
|
||||
@@ -573,7 +573,7 @@ process_upload_data (void *cls,
|
||||
free (uc->filename);
|
||||
uc->filename = NULL;
|
||||
}
|
||||
return MHD_NO;
|
||||
return MHD_NO;
|
||||
}
|
||||
return MHD_YES;
|
||||
}
|
||||
@@ -610,13 +610,13 @@ response_completed_callback (void *cls,
|
||||
(void) close (uc->fd);
|
||||
if (NULL != uc->filename)
|
||||
{
|
||||
fprintf (stderr,
|
||||
fprintf (stderr,
|
||||
"Upload of file `%s' failed (incomplete or aborted), removing file.\n",
|
||||
uc->filename);
|
||||
(void) unlink (uc->filename);
|
||||
}
|
||||
}
|
||||
if (NULL != uc->filename)
|
||||
if (NULL != uc->filename)
|
||||
free (uc->filename);
|
||||
free (uc);
|
||||
}
|
||||
@@ -624,7 +624,7 @@ response_completed_callback (void *cls,
|
||||
|
||||
/**
|
||||
* Return the current directory listing.
|
||||
*
|
||||
*
|
||||
* @param connection connection to return the directory for
|
||||
* @return MHD_YES on success, MHD_NO on error
|
||||
*/
|
||||
@@ -635,12 +635,12 @@ return_directory_response (struct MHD_Connection *connection)
|
||||
|
||||
(void) pthread_mutex_lock (&mutex);
|
||||
if (NULL == cached_directory_response)
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||
internal_error_response);
|
||||
else
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_OK,
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_OK,
|
||||
cached_directory_response);
|
||||
(void) pthread_mutex_unlock (&mutex);
|
||||
return ret;
|
||||
@@ -657,7 +657,7 @@ return_directory_response (struct MHD_Connection *connection)
|
||||
* @param version HTTP version
|
||||
* @param upload_data data from upload (PUT/POST)
|
||||
* @param upload_data_size number of bytes in "upload_data"
|
||||
* @param ptr our context
|
||||
* @param ptr our context
|
||||
* @return MHD_YES on success, MHD_NO to drop connection
|
||||
*/
|
||||
static int
|
||||
@@ -668,11 +668,11 @@ generate_page (void *cls,
|
||||
const char *version,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size, void **ptr)
|
||||
{
|
||||
{
|
||||
struct MHD_Response *response;
|
||||
int ret;
|
||||
int fd;
|
||||
struct stat buf;
|
||||
struct stat buf;
|
||||
|
||||
if (0 != strcmp (url, "/"))
|
||||
{
|
||||
@@ -685,13 +685,13 @@ generate_page (void *cls,
|
||||
return MHD_NO; /* unexpected method (we're not polite...) */
|
||||
if ( (0 == stat (&url[1], &buf)) &&
|
||||
(NULL == strstr (&url[1], "..")) &&
|
||||
('/' != url[1]))
|
||||
('/' != url[1]))
|
||||
fd = open (&url[1], O_RDONLY);
|
||||
else
|
||||
fd = -1;
|
||||
if (-1 == fd)
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
file_not_found_response);
|
||||
/* read beginning of the file to determine mime type */
|
||||
got = read (fd, file_data, sizeof (file_data));
|
||||
@@ -701,12 +701,12 @@ generate_page (void *cls,
|
||||
mime = NULL;
|
||||
(void) lseek (fd, 0, SEEK_SET);
|
||||
|
||||
if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
|
||||
if (NULL == (response = MHD_create_response_from_fd (buf.st_size,
|
||||
fd)))
|
||||
{
|
||||
/* internal error (i.e. out of memory) */
|
||||
(void) close (fd);
|
||||
return MHD_NO;
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
/* add mime type if we had one */
|
||||
@@ -714,8 +714,8 @@ generate_page (void *cls,
|
||||
(void) MHD_add_response_header (response,
|
||||
MHD_HTTP_HEADER_CONTENT_TYPE,
|
||||
mime);
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_OK,
|
||||
ret = MHD_queue_response (connection,
|
||||
MHD_HTTP_OK,
|
||||
response);
|
||||
MHD_destroy_response (response);
|
||||
return ret;
|
||||
@@ -744,11 +744,11 @@ generate_page (void *cls,
|
||||
}
|
||||
*ptr = uc;
|
||||
return MHD_YES;
|
||||
}
|
||||
}
|
||||
if (0 != *upload_data_size)
|
||||
{
|
||||
if (NULL == uc->response)
|
||||
(void) MHD_post_process (uc->pp,
|
||||
(void) MHD_post_process (uc->pp,
|
||||
upload_data,
|
||||
*upload_data_size);
|
||||
*upload_data_size = 0;
|
||||
@@ -764,8 +764,8 @@ generate_page (void *cls,
|
||||
}
|
||||
if (NULL != uc->response)
|
||||
{
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
uc->response);
|
||||
}
|
||||
else
|
||||
@@ -778,8 +778,8 @@ generate_page (void *cls,
|
||||
return return_directory_response (connection);
|
||||
|
||||
/* unexpected request, refuse */
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
return MHD_queue_response (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
request_refused_response);
|
||||
}
|
||||
|
||||
@@ -837,7 +837,7 @@ main (int argc, char *const *argv)
|
||||
|
||||
if ( (argc != 2) ||
|
||||
(1 != sscanf (argv[1], "%u", &port)) ||
|
||||
(UINT16_MAX < port) )
|
||||
(UINT16_MAX < port) )
|
||||
{
|
||||
fprintf (stderr,
|
||||
"%s PORT\n", argv[0]);
|
||||
@@ -864,14 +864,14 @@ main (int argc, char *const *argv)
|
||||
mark_as_html (internal_error_response);
|
||||
update_directory ();
|
||||
d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG
|
||||
#if EPOLL_SUPPORT
|
||||
#if EPOLL_SUPPORT
|
||||
| MHD_USE_EPOLL_LINUX_ONLY
|
||||
#endif
|
||||
,
|
||||
port,
|
||||
NULL, NULL,
|
||||
&generate_page, NULL,
|
||||
MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
|
||||
NULL, NULL,
|
||||
&generate_page, NULL,
|
||||
MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (256 * 1024),
|
||||
#if PRODUCTION
|
||||
MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) (64),
|
||||
#endif
|
||||
|
||||
@@ -774,7 +774,24 @@ enum MHD_OPTION
|
||||
* Increment to use for growing the read buffer (followed by a
|
||||
* `size_t`). Must fit within #MHD_OPTION_CONNECTION_MEMORY_LIMIT.
|
||||
*/
|
||||
MHD_OPTION_CONNECTION_MEMORY_INCREMENT = 21
|
||||
MHD_OPTION_CONNECTION_MEMORY_INCREMENT = 21,
|
||||
|
||||
/**
|
||||
* Use a callback to determine which X.509 certificate should be
|
||||
* used for a given HTTPS connection. This option should be
|
||||
* followed by a argument of type `gnutls_certificate_retrieve_function2 *`.
|
||||
* This option provides an
|
||||
* alternative to #MHD_OPTION_HTTPS_MEM_KEY,
|
||||
* #MHD_OPTION_HTTPS_MEM_CERT. You must use this version if
|
||||
* multiple domains are to be hosted at the same IP address using
|
||||
* TLS's Server Name Indication (SNI) extension. In this case,
|
||||
* the callback is expected to select the correct certificate
|
||||
* based on the SNI information provided. The callback is expected
|
||||
* to access the SNI data using `gnutls_server_name_get()`.
|
||||
* Using this option requires GnuTLS 3.0 or higher.
|
||||
*/
|
||||
MHD_OPTION_HTTPS_CERT_CALLBACK = 22
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -471,6 +471,13 @@ MHD_init_daemon_certificate (struct MHD_Daemon *daemon)
|
||||
gnutls_datum_t key;
|
||||
gnutls_datum_t cert;
|
||||
|
||||
#if GNUTLS_VERSION_MAJOR >= 3
|
||||
if (NULL != daemon->cert_callback)
|
||||
{
|
||||
gnutls_certificate_set_retrieve_function2 (daemon->x509_cred,
|
||||
daemon->cert_callback);
|
||||
}
|
||||
#endif
|
||||
if (NULL != daemon->https_mem_trust)
|
||||
{
|
||||
cert.data = (unsigned char *) daemon->https_mem_trust;
|
||||
@@ -499,6 +506,10 @@ MHD_init_daemon_certificate (struct MHD_Daemon *daemon)
|
||||
&cert, &key,
|
||||
GNUTLS_X509_FMT_PEM);
|
||||
}
|
||||
#if GNUTLS_VERSION_MAJOR >= 3
|
||||
if (NULL != daemon->cert_callback)
|
||||
return 0;
|
||||
#endif
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon, "You need to specify a certificate and key location\n");
|
||||
#endif
|
||||
@@ -2900,6 +2911,18 @@ parse_options_va (struct MHD_Daemon *daemon,
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MHD_OPTION_HTTPS_CERT_CALLBACK:
|
||||
#if GNUTLS_VERSION_MAJOR < 3
|
||||
#if HAVE_MESSAGES
|
||||
MHD_DLOG (daemon,
|
||||
"MHD_OPTION_HTTPS_CERT_CALLBACK requires building MHD with GnuTLS >= 3.0\n");
|
||||
#endif
|
||||
return MHD_NO;
|
||||
#else
|
||||
if (0 != (daemon->options & MHD_USE_SSL))
|
||||
daemon->cert_callback = va_arg (ap, gnutls_certificate_retrieve_function2 *);
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef DAUTH_SUPPORT
|
||||
case MHD_OPTION_DIGEST_AUTH_RANDOM:
|
||||
@@ -2974,6 +2997,7 @@ parse_options_va (struct MHD_Daemon *daemon,
|
||||
case MHD_OPTION_HTTPS_MEM_TRUST:
|
||||
case MHD_OPTION_HTTPS_PRIORITIES:
|
||||
case MHD_OPTION_ARRAY:
|
||||
case MHD_OPTION_HTTPS_CERT_CALLBACK:
|
||||
if (MHD_YES != parse_options (daemon,
|
||||
servaddr,
|
||||
opt,
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
#include "microhttpd.h"
|
||||
#if HTTPS_SUPPORT
|
||||
#include <gnutls/gnutls.h>
|
||||
#if GNUTLS_VERSION_MAJOR >= 3
|
||||
#include <gnutls/abstract.h>
|
||||
#endif
|
||||
#endif
|
||||
#if EPOLL_SUPPORT
|
||||
#include <sys/epoll.h>
|
||||
@@ -1161,6 +1164,14 @@ struct MHD_Daemon
|
||||
*/
|
||||
gnutls_dh_params_t dh_params;
|
||||
|
||||
#if GNUTLS_VERSION_MAJOR >= 3
|
||||
/**
|
||||
* Function that can be used to obtain the certificate. Needed
|
||||
* for SNI support. See #MHD_OPTION_HTTPS_CERT_CALLBACK.
|
||||
*/
|
||||
gnutls_certificate_retrieve_function2 *cert_callback;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Pointer to our SSL/TLS key (in ASCII) in memory.
|
||||
*/
|
||||
|
||||
@@ -19,6 +19,7 @@ check_PROGRAMS = \
|
||||
test_tls_authentication \
|
||||
test_https_multi_daemon \
|
||||
test_https_get \
|
||||
test_https_sni \
|
||||
test_https_get_select \
|
||||
test_https_get_parallel \
|
||||
test_https_get_parallel_threads \
|
||||
@@ -32,6 +33,7 @@ TESTS = \
|
||||
test_tls_options \
|
||||
test_https_multi_daemon \
|
||||
test_https_get \
|
||||
test_https_sni \
|
||||
test_https_get_select \
|
||||
test_https_get_parallel \
|
||||
test_https_get_parallel_threads \
|
||||
@@ -113,6 +115,14 @@ test_https_get_LDADD = \
|
||||
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
|
||||
@LIBCURL@ -lgnutls @LIBGCRYPT_LIBS@
|
||||
|
||||
test_https_sni_SOURCES = \
|
||||
test_https_sni.c \
|
||||
tls_test_common.c
|
||||
test_https_sni_LDADD = \
|
||||
$(top_builddir)/src/testcurl/libcurl_version_check.a \
|
||||
$(top_builddir)/src/microhttpd/libmicrohttpd.la \
|
||||
@LIBCURL@ -lgnutls @LIBGCRYPT_LIBS@
|
||||
|
||||
test_https_get_select_SOURCES = \
|
||||
test_https_get_select.c \
|
||||
tls_test_common.c
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWTCCAcICCQDc4McLp7j56DANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJa
|
||||
WjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMQ4wDAYDVQQDDAVob3N0MTEZMBcGCSqGSIb3DQEJARYKdGVzdEBo
|
||||
b3N0MTAgFw0xMzExMTcxNTE2MzdaGA8yMTEzMTAyNDE1MTYzN1owcDELMAkGA1UE
|
||||
BhMCWloxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
|
||||
ZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFaG9zdDExGTAXBgkqhkiG9w0BCQEWCnRl
|
||||
c3RAaG9zdDEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKxYiRUzfQnekQn3
|
||||
6e+hP/mt/JEkiFzX5TV+E19ue2v4tc7lf+SoLEk2dVt5tGQkHjIGeFFNwCLrgXoi
|
||||
h3KfP4R1IYe7NFbM+lFVwPceF3inJ75dZD80BxaXQANeh0yC/DhaVJUFNaof2S4+
|
||||
7xd8zTL6M11gME+XmR8uaDvW7EBtAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAf62m
|
||||
Nstj9p9u8T5A5fRnJWfoglH/zfm7IHzht0Wi047O3NFZJ0pOPqV97HuErUA5oBGg
|
||||
qswnyRGyGMcvL08Bki7Q6NkY7K0ON3lq+ofTkIAHlOKMF+Y/otbjuIDHBfo63tmE
|
||||
uOcr8XDQGu9R0cfh+qLgicJQd/8cFBhxsL0ls6I=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICXQIBAAKBgQCsWIkVM30J3pEJ9+nvoT/5rfyRJIhc1+U1fhNfbntr+LXO5X/k
|
||||
qCxJNnVbebRkJB4yBnhRTcAi64F6Iodynz+EdSGHuzRWzPpRVcD3Hhd4pye+XWQ/
|
||||
NAcWl0ADXodMgvw4WlSVBTWqH9kuPu8XfM0y+jNdYDBPl5kfLmg71uxAbQIDAQAB
|
||||
AoGBAJvq9QmjLSnymtCj4pYSEai2iNpebKdiAlEkoC4j67DArupgohWhN398ryt0
|
||||
rYgzTMYBKHSVnI969AYkmtlNzM1yNckRQb/G/tWrkl9re28y2nbAExtHbvLoTk2C
|
||||
a/EEl1Op+JZNzLoSje7IQMVZoArD3d4aUbfux4XzlO2eRNmZAkEA2pV49QgcOTOJ
|
||||
PrR5cgekonNdeMtkbZm9dhxgDk9IsYkC0iOxjn/IbeCQN3wuTQ5/yLoiiQ/CQ8w5
|
||||
JndF/XpICwJBAMnY37BSRb+XKZeJWP0yjqyFJwzHXkh6IsoSF2OOXSixdiMpthLh
|
||||
IPzvo6Qxsnha4VvwuDxljHzQFPgMT//CTGcCQQDMs9S+LKU50JDEX4Goj43X8RBl
|
||||
cp0Poz3yYap3XDqowLYalADRgcvzUq3cuHgoA98Z3W9ASrjUg2o2ItcyBhV3AkAK
|
||||
bCBgwl7Hnc6P/I+Tw2CKl/WEO2cq5uOU+4opodg9maw39JdqMiW56cXRXJ+Sh17L
|
||||
mIpq0/OFHll21WvsEORRAkAnDDn/vmW25PSxPVY7tKKJCCkmtBeLQpySfpDgBF+O
|
||||
QvvokKs2COivc50rmOYNvD1WSsAOspdaSoZUgFw5ikti
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICWTCCAcICCQCJ9nhDYTUBKjANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJa
|
||||
WjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||
cyBQdHkgTHRkMQ4wDAYDVQQDDAVob3N0MjEZMBcGCSqGSIb3DQEJARYKdGVzdEBo
|
||||
b3N0MjAgFw0xMzExMTcxNTE2NDNaGA8yMTEzMTAyNDE1MTY0M1owcDELMAkGA1UE
|
||||
BhMCWloxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
|
||||
ZGdpdHMgUHR5IEx0ZDEOMAwGA1UEAwwFaG9zdDIxGTAXBgkqhkiG9w0BCQEWCnRl
|
||||
c3RAaG9zdDIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALVK8QKMvU96iNL2
|
||||
66PKm6xXw9NPHDn+o1TLF1CQRxXMrBYUrObk0961+3n3Z3BXOFHKfSV4E55CpVyz
|
||||
D1Wcadlt3B9z3ke3HOi0lEa1xNJTMQK/QT3Fx/NURmNg5s9HAsqY4ocb9KHaF5Ex
|
||||
0TgC0L0aRP0cK1x2TgPEHBNcgGl9AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAEXOi
|
||||
9rSmVrTN5olIdowctr1vWbGwRCjCnAFXDsqakcDASNthr15LB5kr/mrA3olJjbZh
|
||||
o+JDvWMY6FN8r1QXW0RL9/obbHxtJpwvAmYVMY9jrR8Rpo38p4RfXlN85g3q9PVx
|
||||
5IGLaOqLf4hSnKArFL/fzXwxX9b5HBCKlXfiuqM=
|
||||
-----END CERTIFICATE-----
|
||||
@@ -0,0 +1,15 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIICWwIBAAKBgQC1SvECjL1PeojS9uujypusV8PTTxw5/qNUyxdQkEcVzKwWFKzm
|
||||
5NPetft592dwVzhRyn0leBOeQqVcsw9VnGnZbdwfc95HtxzotJRGtcTSUzECv0E9
|
||||
xcfzVEZjYObPRwLKmOKHG/Sh2heRMdE4AtC9GkT9HCtcdk4DxBwTXIBpfQIDAQAB
|
||||
AoGAR5Do6TfDt69IefdNeCAQKg2PWUg+fUpfEacGciAyX5GnUSQiSReF58HxHumi
|
||||
ZL+ZlPgZRQRMwknO23Q4FnSjd66A3E9iHLqkWxRFJWME6E7zgtBrIjctnNu9uYM9
|
||||
cw4R6qmXOL7C5sK00KXF2ep8+s+JjrZz61o85QnGGRYA94ECQQDbG6f1B8NKY9T1
|
||||
1GDR/++rJbdTVQlZQcKSXMumpU6V3mEV0O9GkYaZzoYvWa3kx6c0np4karrm3QWa
|
||||
u5E0q1YdAkEA09FPcmzVvIR0+sMWca8QJ/tJUxD6qYo8vLOpO4wt4iTPhGBEU+Q5
|
||||
cgXmde3/plVsp0vYxK/NG5XZkoC1fbuC4QJATRGxRlLwsl3jLoUBeVxY5Q5jKYCj
|
||||
xS2ITwss5vUGa1jJNW9EesH9YmRudoFI1UwU2EFixtRz4Xik3ARV0vzhUQJAfabT
|
||||
50ASxqMYtczW2peMEPurMqCG4d4ES7iUMqPkcBuAErn8rntbbH19igWmOyi/rLp8
|
||||
m6jiFnQdPiAmCbEbYQJAFAKiQl2ZOe3gkSh8MaQilD8Ppog6rod4SQiSmRNsDWPi
|
||||
IxqXneaGDWhzynC9xr4SwuJ9D5VxW1phNyiveDuYXw==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
@@ -0,0 +1,289 @@
|
||||
/*
|
||||
This file is part of libmicrohttpd
|
||||
(C) 2013 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., 59 Temple Place - Suite 330,
|
||||
Boston, MA 02111-1307, 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>
|
||||
#include <gcrypt.h>
|
||||
#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));
|
||||
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;
|
||||
|
||||
gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
|
||||
#ifdef GCRYCTL_INITIALIZATION_FINISHED
|
||||
gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
#endif
|
||||
if (0 != curl_global_init (CURL_GLOBAL_ALL))
|
||||
{
|
||||
fprintf (stderr, "Error: %s\n", strerror (errno));
|
||||
return -1;
|
||||
}
|
||||
load_keys ("host1", "host1.crt", "host1.key");
|
||||
load_keys ("host2", "host2.crt", "host2.key");
|
||||
d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_SSL | MHD_USE_DEBUG,
|
||||
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;
|
||||
}
|
||||
error_count += do_get ("https://host1:4233/");
|
||||
error_count += do_get ("https://host2:4233/");
|
||||
|
||||
MHD_stop_daemon (d);
|
||||
curl_global_cleanup ();
|
||||
return error_count != 0;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
int main ()
|
||||
{
|
||||
fprintf (stderr,
|
||||
"SNI not supported by GnuTLS < 3.0\n");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user