gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] r31648 - in libmicrohttpd: . doc doc/chapters src/examples


From: gnunet
Subject: [GNUnet-SVN] r31648 - in libmicrohttpd: . doc doc/chapters src/examples src/include src/microhttpd src/testcurl/https
Date: Sat, 21 Dec 2013 17:28:54 +0100

Author: grothoff
Date: 2013-12-21 17:28:54 +0100 (Sat, 21 Dec 2013)
New Revision: 31648

Added:
   libmicrohttpd/src/testcurl/https/host1.crt
   libmicrohttpd/src/testcurl/https/host1.key
   libmicrohttpd/src/testcurl/https/host2.crt
   libmicrohttpd/src/testcurl/https/host2.key
   libmicrohttpd/src/testcurl/https/test_https_sni.c
Modified:
   libmicrohttpd/ChangeLog
   libmicrohttpd/doc/chapters/tlsauthentication.inc
   libmicrohttpd/doc/libmicrohttpd-tutorial.texi
   libmicrohttpd/doc/libmicrohttpd.texi
   libmicrohttpd/src/examples/demo.c
   libmicrohttpd/src/include/microhttpd.h
   libmicrohttpd/src/microhttpd/daemon.c
   libmicrohttpd/src/microhttpd/internal.h
   libmicrohttpd/src/testcurl/https/Makefile.am
Log:
add support for SNI

Modified: libmicrohttpd/ChangeLog
===================================================================
--- libmicrohttpd/ChangeLog     2013-12-21 16:27:06 UTC (rev 31647)
+++ libmicrohttpd/ChangeLog     2013-12-21 16:28:54 UTC (rev 31648)
@@ -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

Modified: libmicrohttpd/doc/chapters/tlsauthentication.inc
===================================================================
--- libmicrohttpd/doc/chapters/tlsauthentication.inc    2013-12-21 16:27:06 UTC 
(rev 31647)
+++ libmicrohttpd/doc/chapters/tlsauthentication.inc    2013-12-21 16:28:54 UTC 
(rev 31648)
@@ -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 @@
 
 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 @@
 
 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 @@
 @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 @@
   if (NULL == daemon)
     {
       printf ("%s\n", cert_pem);
-  
+
       free (key_pem);
       free (cert_pem);
-  
+
       return 1;
     }
 @end verbatim
@@ -96,7 +96,7 @@
   MHD_stop_daemon (daemon);
   free (key_pem);
   free (cert_pem);
-  
+
   return 0;
 }
 @end verbatim
@@ -110,18 +110,18 @@
 @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 @@
 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 @@
 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 @@
  *
  * @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 @@
     }
   /* 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 @@
  *                     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 @@
  */
 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 @@
   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 @@
        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 @@
 gnutls_x509_crt_deinit (client_cert);
 @end verbatim
 
+
+
address@hidden 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:
address@hidden
+  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);
address@hidden 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:
+
address@hidden {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)
+
address@hidden @var
address@hidden 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()}.
+
address@hidden pk_algos
+contains a list with server’s acceptable signature algorithms. The certificate 
returned should support the server’s given algorithms.
+
address@hidden pcert
+should contain a single certificate and public or a list of them.
+
address@hidden pcert_length
+is the size of the previous list.
+
address@hidden pkey
+is the private key.
address@hidden table
address@hidden deftypefn
+
+A possible implementation of this callback would look like this:
+
address@hidden
+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;
+}
address@hidden 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:
+
address@hidden
+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);
+}
address@hidden 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.

Modified: libmicrohttpd/doc/libmicrohttpd-tutorial.texi
===================================================================
--- libmicrohttpd/doc/libmicrohttpd-tutorial.texi       2013-12-21 16:27:06 UTC 
(rev 31647)
+++ libmicrohttpd/doc/libmicrohttpd-tutorial.texi       2013-12-21 16:28:54 UTC 
(rev 31648)
@@ -1,10 +1,10 @@
 \input texinfo  @c -*-texinfo-*-
 @finalout
 @setfilename libmicrohttpd-tutorial.info
address@hidden UPDATED 17 July 2012
address@hidden UPDATED-MONTH July 2012
address@hidden EDITION 0.9.22
address@hidden VERSION 0.9.22
address@hidden UPDATED 17 November 2013
address@hidden UPDATED-MONTH November 2013
address@hidden EDITION 0.9.23
address@hidden 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 @@
 @node Top
 @top A Tutorial for GNU libmicrohttpd
 @insertcopying
address@hidden ifnottex     
address@hidden ifnottex
 
 @menu
 * Introduction::
@@ -97,8 +97,8 @@
 @chapter Processing POST data
 @include chapters/processingpost.inc
 
address@hidden Improved processing of POST data 
address@hidden Improved processing of POST data 
address@hidden Improved processing of POST data
address@hidden Improved processing of POST data
 @include chapters/largerpost.inc
 
 @node Session management

Modified: libmicrohttpd/doc/libmicrohttpd.texi
===================================================================
--- libmicrohttpd/doc/libmicrohttpd.texi        2013-12-21 16:27:06 UTC (rev 
31647)
+++ libmicrohttpd/doc/libmicrohttpd.texi        2013-12-21 16:28:54 UTC (rev 
31648)
@@ -701,6 +701,22 @@
 unchanged to gnutls_priority_init.  If this option is not
 specified, ``NORMAL'' is used.
 
address@hidden MHD_OPTION_HTTPS_CERT_CALLBACK
address@hidden SSL
address@hidden TLS
address@hidden 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

Modified: libmicrohttpd/src/examples/demo.c
===================================================================
--- libmicrohttpd/src/examples/demo.c   2013-12-21 16:27:06 UTC (rev 31647)
+++ libmicrohttpd/src/examples/demo.c   2013-12-21 16:28:54 UTC (rev 31648)
@@ -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 @@
    * Response data string.
    */
   char *buf;
-  
+
   /**
    * Number of bytes allocated for 'buf'.
    */
@@ -253,12 +253,12 @@
   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 @@
            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 @@
   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 @@
          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 @@
        }
     }
   /* 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 @@
 {
   char *buf;
   size_t old_len;
-  
+
   if (NULL == *ret)
     old_len = 0;
   else
@@ -471,8 +471,8 @@
                     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 @@
     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 @@
   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 @@
       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 @@
                "%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 @@
                     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 @@
          free (uc->filename);
          uc->filename = NULL;
        }
-      return MHD_NO; 
+      return MHD_NO;
     }
   return MHD_YES;
 }
@@ -610,13 +610,13 @@
     (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 @@
 
 /**
  * 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 @@
 
   (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 @@
  * @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 @@
               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 @@
        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 @@
        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 @@
        (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 @@
            }
          *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 @@
        }
       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 @@
     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 @@
 
   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 @@
   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

Modified: libmicrohttpd/src/include/microhttpd.h
===================================================================
--- libmicrohttpd/src/include/microhttpd.h      2013-12-21 16:27:06 UTC (rev 
31647)
+++ libmicrohttpd/src/include/microhttpd.h      2013-12-21 16:28:54 UTC (rev 
31648)
@@ -774,7 +774,24 @@
    * 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
+
 };
 
 

Modified: libmicrohttpd/src/microhttpd/daemon.c
===================================================================
--- libmicrohttpd/src/microhttpd/daemon.c       2013-12-21 16:27:06 UTC (rev 
31647)
+++ libmicrohttpd/src/microhttpd/daemon.c       2013-12-21 16:28:54 UTC (rev 
31648)
@@ -471,6 +471,13 @@
   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 @@
                                                  &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,7 +2911,19 @@
              }
            }
           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:
          daemon->digest_auth_rand_size = va_arg (ap, size_t);
@@ -2974,6 +2997,7 @@
                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,

Modified: libmicrohttpd/src/microhttpd/internal.h
===================================================================
--- libmicrohttpd/src/microhttpd/internal.h     2013-12-21 16:27:06 UTC (rev 
31647)
+++ libmicrohttpd/src/microhttpd/internal.h     2013-12-21 16:28:54 UTC (rev 
31648)
@@ -31,7 +31,10 @@
 #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>
 #endif
@@ -1161,7 +1164,15 @@
    */
   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.
    */
   const char *https_mem_key;

Modified: libmicrohttpd/src/testcurl/https/Makefile.am
===================================================================
--- libmicrohttpd/src/testcurl/https/Makefile.am        2013-12-21 16:27:06 UTC 
(rev 31647)
+++ libmicrohttpd/src/testcurl/https/Makefile.am        2013-12-21 16:28:54 UTC 
(rev 31648)
@@ -19,6 +19,7 @@
   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 @@
   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 @@
   $(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

Added: libmicrohttpd/src/testcurl/https/host1.crt
===================================================================
--- libmicrohttpd/src/testcurl/https/host1.crt                          (rev 0)
+++ libmicrohttpd/src/testcurl/https/host1.crt  2013-12-21 16:28:54 UTC (rev 
31648)
@@ -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-----

Added: libmicrohttpd/src/testcurl/https/host1.key
===================================================================
--- libmicrohttpd/src/testcurl/https/host1.key                          (rev 0)
+++ libmicrohttpd/src/testcurl/https/host1.key  2013-12-21 16:28:54 UTC (rev 
31648)
@@ -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-----

Added: libmicrohttpd/src/testcurl/https/host2.crt
===================================================================
--- libmicrohttpd/src/testcurl/https/host2.crt                          (rev 0)
+++ libmicrohttpd/src/testcurl/https/host2.crt  2013-12-21 16:28:54 UTC (rev 
31648)
@@ -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-----

Added: libmicrohttpd/src/testcurl/https/host2.key
===================================================================
--- libmicrohttpd/src/testcurl/https/host2.key                          (rev 0)
+++ libmicrohttpd/src/testcurl/https/host2.key  2013-12-21 16:28:54 UTC (rev 
31648)
@@ -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-----

Added: libmicrohttpd/src/testcurl/https/test_https_sni.c
===================================================================
--- libmicrohttpd/src/testcurl/https/test_https_sni.c                           
(rev 0)
+++ libmicrohttpd/src/testcurl/https/test_https_sni.c   2013-12-21 16:28:54 UTC 
(rev 31648)
@@ -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, &copyBuffer);
+  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




reply via email to

[Prev in Thread] Current Thread [Next in Thread]