1We left the basic authentication chapter with the unsatisfactory conclusion that
2any traffic, including the credentials, could be intercepted by anyone between
3the browser client and the server. Protecting the data while it is sent over
4unsecured lines will be the goal of this chapter.
5
6Since version 0.4, the @emph{MHD} library includes support for encrypting the
7traffic by employing SSL/TSL. If @emph{GNU libmicrohttpd} has been configured to
8support these, encryption and decryption can be applied transparently on the
9data being sent, with only minimal changes to the actual source code of the example.
10
11
12@heading Preparation
13
14First, a private key for the server will be generated. With this key, the server
15will later be able to authenticate itself to the client---preventing anyone else
16from stealing the password by faking its identity. The @emph{OpenSSL} suite, which
17is available on many operating systems, can generate such a key. For the scope of
18this tutorial, we will be content with a 1024 bit key:
19@verbatim
20> openssl genrsa -out server.key 1024
21@end verbatim
22@noindent
23
24In addition to the key, a certificate describing the server in human readable tokens
25is also needed. This certificate will be attested with our aforementioned key. In this way,
26we obtain a self-signed certificate, valid for one year.
27
28@verbatim
29> openssl req -days 365 -out server.pem -new -x509 -key server.key
30@end verbatim
31@noindent
32
33To avoid unnecessary error messages in the browser, the certificate needs to
34have a name that matches the @emph{URI}, for example, "localhost" or the domain.
35If you plan to have a publicly reachable server, you will need to ask a trusted third party,
36called @emph{Certificate Authority}, or @emph{CA}, to attest the certificate for you. This way,
37any visitor can make sure the server's identity is real.
38
39Whether the server's certificate is signed by us or a third party, once it has been accepted
40by the client, both sides will be communicating over encrypted channels. From this point on,
41it is the client's turn to authenticate itself. But this has already been implemented in the basic
42authentication scheme.
43
44
45@heading Changing the source code
46
47We merely have to extend the server program so that it loads the two files into memory,
48
49@verbatim
50int
51main ()
52{
53  struct MHD_Daemon *daemon;
54  char *key_pem;
55  char *cert_pem;
56
57  key_pem = load_file (SERVERKEYFILE);
58  cert_pem = load_file (SERVERCERTFILE);
59
60  if ((key_pem == NULL) || (cert_pem == NULL))
61  {
62    printf ("The key/certificate files could not be read.\n");
63    return 1;
64  }
65@end verbatim
66@noindent
67
68and then we point the @emph{MHD} daemon to it upon initalization.
69@verbatim
70
71  daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
72  	   		     PORT, NULL, NULL,
73                             &answer_to_connection, NULL,
74                             MHD_OPTION_HTTPS_MEM_KEY, key_pem,
75                             MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
76                             MHD_OPTION_END);
77
78  if (NULL == daemon)
79    {
80      printf ("%s\n", cert_pem);
81
82      free (key_pem);
83      free (cert_pem);
84
85      return 1;
86    }
87@end verbatim
88@noindent
89
90
91The rest consists of little new besides some additional memory cleanups.
92@verbatim
93
94  getchar ();
95
96  MHD_stop_daemon (daemon);
97  free (key_pem);
98  free (cert_pem);
99
100  return 0;
101}
102@end verbatim
103@noindent
104
105
106The rather unexciting file loader can be found in the complete example @code{tlsauthentication.c}.
107
108
109@heading Remarks
110@itemize @bullet
111@item
112While the standard @emph{HTTP} port is 80, it is 443 for @emph{HTTPS}. The common internet browsers assume
113standard @emph{HTTP} if they are asked to access other ports than these. Therefore, you will have to type
114@code{https://localhost:8888} explicitly when you test the example, or the browser will not know how to
115handle the answer properly.
116
117@item
118The remaining weak point is the question how the server will be trusted initially. Either a @emph{CA} signs the
119certificate or the client obtains the key over secure means. Anyway, the clients have to be aware (or configured)
120that they should not accept certificates of unknown origin.
121
122@item
123The introduced method of certificates makes it mandatory to set an expiration date---making it less feasible to
124hardcode certificates in embedded devices.
125
126@item
127The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists
128both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}.
129
130@end itemize
131
132
133@heading Client authentication
134
135You can also use MHD to authenticate the client via SSL/TLS certificates
136(as an alternative to using the password-based Basic or Digest authentication).
137To do this, you will need to link your application against @emph{gnutls}.
138Next, when you start the MHD daemon, you must specify the root CA that you're
139willing to trust:
140@verbatim
141  daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
142  	   		     PORT, NULL, NULL,
143                             &answer_to_connection, NULL,
144                             MHD_OPTION_HTTPS_MEM_KEY, key_pem,
145                             MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
146			     MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem,
147                             MHD_OPTION_END);
148@end verbatim
149
150With this, you can then obtain client certificates for each session.
151In order to obtain the identity of the client, you first need to
152obtain the raw GnuTLS session handle from @emph{MHD} using
153@code{MHD_get_connection_info}.
154
155@verbatim
156#include <gnutls/gnutls.h>
157#include <gnutls/x509.h>
158
159gnutls_session_t tls_session;
160union MHD_ConnectionInfo *ci;
161
162ci = MHD_get_connection_info (connection,
163                              MHD_CONNECTION_INFO_GNUTLS_SESSION);
164tls_session = ci->tls_session;
165@end verbatim
166
167You can then extract the client certificate:
168
169@verbatim
170/**
171 * Get the client's certificate
172 *
173 * @param tls_session the TLS session
174 * @return NULL if no valid client certificate could be found, a pointer
175 *  	to the certificate if found
176 */
177static gnutls_x509_crt_t
178get_client_certificate (gnutls_session_t tls_session)
179{
180  unsigned int listsize;
181  const gnutls_datum_t * pcert;
182  gnutls_certificate_status_t client_cert_status;
183  gnutls_x509_crt_t client_cert;
184
185  if (tls_session == NULL)
186    return NULL;
187  if (gnutls_certificate_verify_peers2(tls_session,
188				       &client_cert_status))
189    return NULL;
190  pcert = gnutls_certificate_get_peers(tls_session,
191				       &listsize);
192  if ( (pcert == NULL) ||
193       (listsize == 0))
194    {
195      fprintf (stderr,
196	       "Failed to retrieve client certificate chain\n");
197      return NULL;
198    }
199  if (gnutls_x509_crt_init(&client_cert))
200    {
201      fprintf (stderr,
202	       "Failed to initialize client certificate\n");
203      return NULL;
204    }
205  /* Note that by passing values between 0 and listsize here, you
206     can get access to the CA's certs */
207  if (gnutls_x509_crt_import(client_cert,
208			     &pcert[0],
209			     GNUTLS_X509_FMT_DER))
210    {
211      fprintf (stderr,
212	       "Failed to import client certificate\n");
213      gnutls_x509_crt_deinit(client_cert);
214      return NULL;
215    }
216  return client_cert;
217}
218@end verbatim
219
220Using the client certificate, you can then get the client's distinguished name
221and alternative names:
222
223@verbatim
224/**
225 * Get the distinguished name from the client's certificate
226 *
227 * @param client_cert the client certificate
228 * @return NULL if no dn or certificate could be found, a pointer
229 * 			to the dn if found
230 */
231char *
232cert_auth_get_dn(gnutls_x509_crt_c client_cert)
233{
234  char* buf;
235  size_t lbuf;
236
237  lbuf = 0;
238  gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf);
239  buf = malloc(lbuf);
240  if (buf == NULL)
241    {
242      fprintf (stderr,
243	       "Failed to allocate memory for certificate dn\n");
244      return NULL;
245    }
246  gnutls_x509_crt_get_dn(client_cert, buf, &lbuf);
247  return buf;
248}
249
250
251/**
252 * Get the alternative name of specified type from the client's certificate
253 *
254 * @param client_cert the client certificate
255 * @param nametype The requested name type
256 * @param index The position of the alternative name if multiple names are
257 * 			matching the requested type, 0 for the first matching name
258 * @return NULL if no matching alternative name could be found, a pointer
259 * 			to the alternative name if found
260 */
261char *
262MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
263			   int nametype,
264			   unsigned int index)
265{
266  char* buf;
267  size_t lbuf;
268  unsigned int seq;
269  unsigned int subseq;
270  unsigned int type;
271  int result;
272
273  subseq = 0;
274  for (seq=0;;seq++)
275    {
276      lbuf = 0;
277      result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf,
278						     &type, NULL);
279      if (result == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
280	return NULL;
281      if (nametype != (int) type)
282	continue;
283      if (subseq == index)
284	break;
285      subseq++;
286    }
287  buf = malloc(lbuf);
288  if (buf == NULL)
289    {
290      fprintf (stderr,
291	       "Failed to allocate memory for certificate alt name\n");
292      return NULL;
293    }
294  result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
295						 seq,
296						 buf,
297						 &lbuf,
298						 NULL, NULL);
299  if (result != nametype)
300    {
301      fprintf (stderr,
302	       "Unexpected return value from gnutls: %d\n",
303	       result);
304      free (buf);
305      return NULL;
306    }
307  return buf;
308}
309@end verbatim
310
311Finally, you should release the memory associated with the client
312certificate:
313
314@verbatim
315gnutls_x509_crt_deinit (client_cert);
316@end verbatim
317
318
319
320@heading Using TLS Server Name Indication (SNI)
321
322SNI enables hosting multiple domains under one IP address with TLS.  So
323SNI is the TLS-equivalent of virtual hosting.  To use SNI with MHD, you
324need at least GnuTLS 3.0.  The main change compared to the simple hosting
325of one domain is that you need to provide a callback instead of the key
326and certificate.  For example, when you start the MHD daemon, you could
327do this:
328@verbatim
329  daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
330  	   		     PORT, NULL, NULL,
331                             &answer_to_connection, NULL,
332                             MHD_OPTION_HTTPS_CERT_CALLBACK, &sni_callback,
333                             MHD_OPTION_END);
334@end verbatim
335Here, @code{sni_callback} is the name of a function that you will have to
336implement to retrieve the X.509 certificate for an incoming connection.
337The callback has type @code{gnutls_certificate_retrieve_function2} and
338is documented in the GnuTLS API for the @code{gnutls_certificate_set_retrieve_function2}
339as follows:
340
341@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)
342
343@table @var
344@item req_ca_cert
345is 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()}.
346
347@item pk_algos
348contains a list with server’s acceptable signature algorithms. The certificate returned should support the server’s given algorithms.
349
350@item pcert
351should contain a single certificate and public or a list of them.
352
353@item pcert_length
354is the size of the previous list.
355
356@item pkey
357is the private key.
358@end table
359@end deftypefn
360
361A possible implementation of this callback would look like this:
362
363@verbatim
364struct Hosts
365{
366  struct Hosts *next;
367  const char *hostname;
368  gnutls_pcert_st pcrt;
369  gnutls_privkey_t key;
370};
371
372static struct Hosts *hosts;
373
374int
375sni_callback (gnutls_session_t session,
376              const gnutls_datum_t* req_ca_dn,
377              int nreqs,
378              const gnutls_pk_algorithm_t* pk_algos,
379              int pk_algos_length,
380              gnutls_pcert_st** pcert,
381              unsigned int *pcert_length,
382              gnutls_privkey_t * pkey)
383{
384  char name[256];
385  size_t name_len;
386  struct Hosts *host;
387  unsigned int type;
388
389  name_len = sizeof (name);
390  if (GNUTLS_E_SUCCESS !=
391      gnutls_server_name_get (session,
392                              name,
393                              &name_len,
394                              &type,
395                              0 /* index */))
396    return -1;
397  for (host = hosts; NULL != host; host = host->next)
398    if (0 == strncmp (name, host->hostname, name_len))
399      break;
400  if (NULL == host)
401    {
402      fprintf (stderr,
403               "Need certificate for %.*s\n",
404               (int) name_len,
405               name);
406      return -1;
407    }
408  fprintf (stderr,
409           "Returning certificate for %.*s\n",
410           (int) name_len,
411           name);
412  *pkey = host->key;
413  *pcert_length = 1;
414  *pcert = &host->pcrt;
415  return 0;
416}
417@end verbatim
418
419Note that MHD cannot offer passing a closure or any other additional information
420to this callback, as the GnuTLS API unfortunately does not permit this at this
421point.
422
423The @code{hosts} list can be initialized by loading the private keys and X.509
424certificats from disk as follows:
425
426@verbatim
427static void
428load_keys(const char *hostname,
429          const char *CERT_FILE,
430          const char *KEY_FILE)
431{
432  int ret;
433  gnutls_datum_t data;
434  struct Hosts *host;
435
436  host = malloc (sizeof (struct Hosts));
437  host->hostname = hostname;
438  host->next = hosts;
439  hosts = host;
440
441  ret = gnutls_load_file (CERT_FILE, &data);
442  if (ret < 0)
443  {
444    fprintf (stderr,
445             "*** Error loading certificate file %s.\n",
446             CERT_FILE);
447    exit(1);
448  }
449  ret =
450    gnutls_pcert_import_x509_raw (&host->pcrt, &data, GNUTLS_X509_FMT_PEM,
451                                  0);
452  if (ret < 0)
453  {
454    fprintf(stderr,
455            "*** Error loading certificate file: %s\n",
456            gnutls_strerror (ret));
457    exit(1);
458  }
459  gnutls_free (data.data);
460
461  ret = gnutls_load_file (KEY_FILE, &data);
462  if (ret < 0)
463  {
464    fprintf (stderr,
465             "*** Error loading key file %s.\n",
466             KEY_FILE);
467    exit(1);
468  }
469
470  gnutls_privkey_init (&host->key);
471  ret =
472    gnutls_privkey_import_x509_raw (host->key,
473                                    &data, GNUTLS_X509_FMT_PEM,
474                                    NULL, 0);
475  if (ret < 0)
476  {
477    fprintf (stderr,
478             "*** Error loading key file: %s\n",
479             gnutls_strerror (ret));
480    exit(1);
481  }
482  gnutls_free (data.data);
483}
484@end verbatim
485
486The code above was largely lifted from GnuTLS.  You can find other
487methods for initializing certificates and keys in the GnuTLS manual
488and source code.
489