aboutsummaryrefslogtreecommitdiff
path: root/doc/chapters/tlsauthentication.inc
diff options
context:
space:
mode:
Diffstat (limited to 'doc/chapters/tlsauthentication.inc')
-rw-r--r--doc/chapters/tlsauthentication.inc247
1 files changed, 209 insertions, 38 deletions
diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc
index f945544e..c1b66735 100644
--- a/doc/chapters/tlsauthentication.inc
+++ b/doc/chapters/tlsauthentication.inc
@@ -1,6 +1,6 @@
1We left the basic authentication chapter with the unsatisfactory conclusion that 1We left the basic authentication chapter with the unsatisfactory conclusion that
2any traffic, including the credentials, could be intercepted by anyone between 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 3the browser client and the server. Protecting the data while it is sent over
4unsecured lines will be the goal of this chapter. 4unsecured lines will be the goal of this chapter.
5 5
6Since version 0.4, the @emph{MHD} library includes support for encrypting the 6Since 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:
23 23
24In addition to the key, a certificate describing the server in human readable tokens 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, 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. 26we obtain a self-signed certificate, valid for one year.
27 27
28@verbatim 28@verbatim
29> openssl req -days 365 -out server.pem -new -x509 -key server.key 29> 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.
38 38
39Whether the server's certificate is signed by us or a third party, once it has been accepted 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, 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 41it is the client's turn to authenticate itself. But this has already been implemented in the basic
42authentication scheme. 42authentication scheme.
43 43
44 44
@@ -65,12 +65,12 @@ main ()
65@end verbatim 65@end verbatim
66@noindent 66@noindent
67 67
68and then we point the @emph{MHD} daemon to it upon initalization. 68and then we point the @emph{MHD} daemon to it upon initalization.
69@verbatim 69@verbatim
70 70
71 daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, 71 daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
72 PORT, NULL, NULL, 72 PORT, NULL, NULL,
73 &answer_to_connection, NULL, 73 &answer_to_connection, NULL,
74 MHD_OPTION_HTTPS_MEM_KEY, key_pem, 74 MHD_OPTION_HTTPS_MEM_KEY, key_pem,
75 MHD_OPTION_HTTPS_MEM_CERT, cert_pem, 75 MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
76 MHD_OPTION_END); 76 MHD_OPTION_END);
@@ -78,10 +78,10 @@ and then we point the @emph{MHD} daemon to it upon initalization.
78 if (NULL == daemon) 78 if (NULL == daemon)
79 { 79 {
80 printf ("%s\n", cert_pem); 80 printf ("%s\n", cert_pem);
81 81
82 free (key_pem); 82 free (key_pem);
83 free (cert_pem); 83 free (cert_pem);
84 84
85 return 1; 85 return 1;
86 } 86 }
87@end verbatim 87@end verbatim
@@ -96,7 +96,7 @@ The rest consists of little new besides some additional memory cleanups.
96 MHD_stop_daemon (daemon); 96 MHD_stop_daemon (daemon);
97 free (key_pem); 97 free (key_pem);
98 free (cert_pem); 98 free (cert_pem);
99 99
100 return 0; 100 return 0;
101} 101}
102@end verbatim 102@end verbatim
@@ -110,18 +110,18 @@ The rather unexciting file loader can be found in the complete example @code{tls
110@itemize @bullet 110@itemize @bullet
111@item 111@item
112While the standard @emph{HTTP} port is 80, it is 443 for @emph{HTTPS}. The common internet browsers assume 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 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 114@code{https://localhost:8888} explicitly when you test the example, or the browser will not know how to
115handle the answer properly. 115handle the answer properly.
116 116
117@item 117@item
118The remaining weak point is the question how the server will be trusted initially. Either a @emph{CA} signs the 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) 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. 120that they should not accept certificates of unknown origin.
121 121
122@item 122@item
123The introduced method of certificates makes it mandatory to set an expiration date---making it less feasible to 123The introduced method of certificates makes it mandatory to set an expiration date---making it less feasible to
124hardcode certificates in embedded devices. 124hardcode certificates in embedded devices.
125 125
126@item 126@item
127The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists 127The 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}.
135You can also use MHD to authenticate the client via SSL/TLS certificates 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). 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}. 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 138Next, when you start the MHD daemon, you must specify the root CA that you're
139willing to trust: 139willing to trust:
140@verbatim 140@verbatim
141 daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, 141 daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
142 PORT, NULL, NULL, 142 PORT, NULL, NULL,
143 &answer_to_connection, NULL, 143 &answer_to_connection, NULL,
144 MHD_OPTION_HTTPS_MEM_KEY, key_pem, 144 MHD_OPTION_HTTPS_MEM_KEY, key_pem,
145 MHD_OPTION_HTTPS_MEM_CERT, cert_pem, 145 MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
146 MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem, 146 MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem,
@@ -159,7 +159,7 @@ obtain the raw GnuTLS session handle from @emph{MHD} using
159gnutls_session_t tls_session; 159gnutls_session_t tls_session;
160union MHD_ConnectionInfo *ci; 160union MHD_ConnectionInfo *ci;
161 161
162ci = MHD_get_connection_info (connection, 162ci = MHD_get_connection_info (connection,
163 MHD_CONNECTION_INFO_GNUTLS_SESSION); 163 MHD_CONNECTION_INFO_GNUTLS_SESSION);
164tls_session = ci->tls_session; 164tls_session = ci->tls_session;
165@end verbatim 165@end verbatim
@@ -172,31 +172,31 @@ You can then extract the client certificate:
172 * 172 *
173 * @param tls_session the TLS session 173 * @param tls_session the TLS session
174 * @return NULL if no valid client certificate could be found, a pointer 174 * @return NULL if no valid client certificate could be found, a pointer
175 * to the certificate if found 175 * to the certificate if found
176 */ 176 */
177static gnutls_x509_crt_t 177static gnutls_x509_crt_t
178get_client_certificate (gnutls_session_t tls_session) 178get_client_certificate (gnutls_session_t tls_session)
179{ 179{
180 unsigned int listsize; 180 unsigned int listsize;
181 const gnutls_datum_t * pcert; 181 const gnutls_datum_t * pcert;
182 gnutls_certificate_status_t client_cert_status; 182 gnutls_certificate_status_t client_cert_status;
183 gnutls_x509_crt_t client_cert; 183 gnutls_x509_crt_t client_cert;
184 184
185 if (tls_session == NULL) 185 if (tls_session == NULL)
186 return NULL; 186 return NULL;
187 if (gnutls_certificate_verify_peers2(tls_session, 187 if (gnutls_certificate_verify_peers2(tls_session,
188 &client_cert_status)) 188 &client_cert_status))
189 return NULL; 189 return NULL;
190 pcert = gnutls_certificate_get_peers(tls_session, 190 pcert = gnutls_certificate_get_peers(tls_session,
191 &listsize); 191 &listsize);
192 if ( (pcert == NULL) || 192 if ( (pcert == NULL) ||
193 (listsize == 0)) 193 (listsize == 0))
194 { 194 {
195 fprintf (stderr, 195 fprintf (stderr,
196 "Failed to retrieve client certificate chain\n"); 196 "Failed to retrieve client certificate chain\n");
197 return NULL; 197 return NULL;
198 } 198 }
199 if (gnutls_x509_crt_init(&client_cert)) 199 if (gnutls_x509_crt_init(&client_cert))
200 { 200 {
201 fprintf (stderr, 201 fprintf (stderr,
202 "Failed to initialize client certificate\n"); 202 "Failed to initialize client certificate\n");
@@ -204,15 +204,15 @@ get_client_certificate (gnutls_session_t tls_session)
204 } 204 }
205 /* Note that by passing values between 0 and listsize here, you 205 /* Note that by passing values between 0 and listsize here, you
206 can get access to the CA's certs */ 206 can get access to the CA's certs */
207 if (gnutls_x509_crt_import(client_cert, 207 if (gnutls_x509_crt_import(client_cert,
208 &pcert[0], 208 &pcert[0],
209 GNUTLS_X509_FMT_DER)) 209 GNUTLS_X509_FMT_DER))
210 { 210 {
211 fprintf (stderr, 211 fprintf (stderr,
212 "Failed to import client certificate\n"); 212 "Failed to import client certificate\n");
213 gnutls_x509_crt_deinit(client_cert); 213 gnutls_x509_crt_deinit(client_cert);
214 return NULL; 214 return NULL;
215 } 215 }
216 return client_cert; 216 return client_cert;
217} 217}
218@end verbatim 218@end verbatim
@@ -229,15 +229,15 @@ and alternative names:
229 * to the dn if found 229 * to the dn if found
230 */ 230 */
231char * 231char *
232cert_auth_get_dn(gnutls_x509_crt_c client_cert) 232cert_auth_get_dn(gnutls_x509_crt_c client_cert)
233{ 233{
234 char* buf; 234 char* buf;
235 size_t lbuf; 235 size_t lbuf;
236 236
237 lbuf = 0; 237 lbuf = 0;
238 gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf); 238 gnutls_x509_crt_get_dn(client_cert, NULL, &lbuf);
239 buf = malloc(lbuf); 239 buf = malloc(lbuf);
240 if (buf == NULL) 240 if (buf == NULL)
241 { 241 {
242 fprintf (stderr, 242 fprintf (stderr,
243 "Failed to allocate memory for certificate dn\n"); 243 "Failed to allocate memory for certificate dn\n");
@@ -260,8 +260,8 @@ cert_auth_get_dn(gnutls_x509_crt_c client_cert)
260 */ 260 */
261char * 261char *
262MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert, 262MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
263 int nametype, 263 int nametype,
264 unsigned int index) 264 unsigned int index)
265{ 265{
266 char* buf; 266 char* buf;
267 size_t lbuf; 267 size_t lbuf;
@@ -271,7 +271,7 @@ MHD_cert_auth_get_alt_name(gnutls_x509_crt_t client_cert,
271 int result; 271 int result;
272 272
273 subseq = 0; 273 subseq = 0;
274 for (seq=0;;seq++) 274 for (seq=0;;seq++)
275 { 275 {
276 lbuf = 0; 276 lbuf = 0;
277 result = gnutls_x509_crt_get_subject_alt_name2(client_cert, seq, NULL, &lbuf, 277 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,
280 return NULL; 280 return NULL;
281 if (nametype != (int) type) 281 if (nametype != (int) type)
282 continue; 282 continue;
283 if (subseq == index) 283 if (subseq == index)
284 break; 284 break;
285 subseq++; 285 subseq++;
286 } 286 }
287 buf = malloc(lbuf); 287 buf = malloc(lbuf);
288 if (buf == NULL) 288 if (buf == NULL)
289 { 289 {
290 fprintf (stderr, 290 fprintf (stderr,
291 "Failed to allocate memory for certificate alt name\n"); 291 "Failed to allocate memory for certificate alt name\n");
292 return NULL; 292 return NULL;
293 } 293 }
294 result = gnutls_x509_crt_get_subject_alt_name2(client_cert, 294 result = gnutls_x509_crt_get_subject_alt_name2(client_cert,
295 seq, 295 seq,
296 buf, 296 buf,
297 &lbuf, 297 &lbuf,
298 NULL, NULL); 298 NULL, NULL);
299 if (result != nametype) 299 if (result != nametype)
300 { 300 {
@@ -315,3 +315,174 @@ certificate:
315gnutls_x509_crt_deinit (client_cert); 315gnutls_x509_crt_deinit (client_cert);
316@end verbatim 316@end verbatim
317 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.