Tutorial: New chapter for SSL/TLS

This commit is contained in:
Sebastian Gerhardt committed 2008-10-11 17:55:14 +00:00
1 parent 8bee920d50
commit 8636c4b308
3 files changed
+394

No files matched your search

+130
View File
@@ -0,0 +1,130 @@
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
unsecured lines will be the goal of this chapter.
Since version 0.4, the @emph{MHD} library includes support for encrypting the
traffic by employing SSL/TSL. If @emph{GNU libmicrohttpd} has been configured to
support these, encryption and decryption can be applied transparently on the
data being sent, with only minimal changes to the actual source code of the example.
@heading Preparation
First, a private key for the server will be generated. With this key, the server
will later be able to authenticate itself to the client---preventing anyone else
from stealing the password by faking its identity. The @emph{OpenSSL} suite, which
is available on many operating systems, can generate such a key. For the scope of
this tutorial, we will be content with a 1024 bit key:
@verbatim
> openssl genrsa -out server.key 1024
@end verbatim
@noindent
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.
@verbatim
> openssl req -days 365 -out server.pem -new -x509 -key server.key
@end verbatim
@noindent
To avoid unnecessary error messages in the browser, the certificate needs to
have a name that matches the @emph{URI}, for example, "localhost" or the domain.
If you plan to have a publicly reachable server, you will need to ask a trusted third party,
called @emph{Certificate Authority}, or @emph{CA}, to attest the certificate for you. This way,
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
authentication scheme.
@heading Changing the source code
We merely have to extend the server program so that it loads the two files into memory,
@verbatim
int
main ()
{
struct MHD_Daemon *daemon;
char *key_pem;
char *cert_pem;
key_pem = load_file (SERVERKEYFILE);
cert_pem = load_file (SERVERCERTFILE);
if ((key_pem == NULL) || (cert_pem == NULL))
{
printf ("The key/certificate files could not be read.\n");
return 1;
}
@end verbatim
@noindent
and then we point the @emph{MHD} daemon to it upon initalization.
@verbatim
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL,
&answer_to_connection, NULL,
MHD_OPTION_HTTPS_MEM_KEY, key_pem,
MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
MHD_OPTION_END);
if (NULL == daemon)
{
printf ("%s\n", cert_pem);
free (key_pem);
free (cert_pem);
return 1;
}
@end verbatim
@noindent
The rest consists of little new besides some additional memory cleanups.
@verbatim
getchar ();
MHD_stop_daemon (daemon);
free (key_pem);
free (cert_pem);
return 0;
}
@end verbatim
@noindent
The rather unexciting file loader can be found in the complete example @code{tlsauthentication.c}.
@heading Remarks
@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
@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)
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.
@item
The cryptographic facilities consume memory space and computing time. For this reason, websites usually consists
both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}.
@end itemize
+253
View File
@@ -0,0 +1,253 @@
#include <platform.h>
#include <microhttpd.h>
#define PORT 8888
#define REALM "\"Maintenance\""
#define USER "a legitimate user"
#define PASSWORD "and his password"
#define SERVERKEYFILE "server.key"
#define SERVERCERTFILE "server.pem"
char *string_to_base64 (const char *message);
long
get_file_size (const char *filename)
{
FILE *fp;
fp = fopen (filename, "rb");
if (fp)
{
long size;
if ((0 != fseek (fp, 0, SEEK_END)) || (-1 == (size = ftell (fp))))
size = 0;
fclose (fp);
return size;
}
else
return 0;
}
char*
load_file (const char* filename)
{
FILE *fp;
char *buffer;
long size;
size = get_file_size (filename);
if (size == 0) return NULL;
fp = fopen (filename, "rb");
if (!fp) return NULL;
buffer = malloc (size);
if (!buffer) {fclose (fp); return NULL;}
if (size != fread (buffer, 1, size, fp))
{
free (buffer);
buffer = NULL;
}
fclose (fp);
return buffer;
}
int
ask_for_authentication (struct MHD_Connection *connection, const char *realm)
{
int ret;
struct MHD_Response *response;
char *headervalue;
const char *strbase = "Basic realm=";
response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO);
if (!response)
return MHD_NO;
headervalue = malloc (strlen (strbase) + strlen (realm) + 1);
if (!headervalue)
return MHD_NO;
strcpy (headervalue, strbase);
strcat (headervalue, realm);
ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue);
free (headervalue);
if (!ret)
{
MHD_destroy_response (response);
return MHD_NO;
}
ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
MHD_destroy_response (response);
return ret;
}
int
is_authenticated (struct MHD_Connection *connection,
const char *username, const char *password)
{
const char *headervalue;
char *expected_b64, *expected;
const char *strbase = "Basic ";
int authenticated;
headervalue =
MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
"Authorization");
if (NULL == headervalue)
return 0;
if (0 != strncmp (headervalue, strbase, strlen (strbase)))
return 0;
expected = malloc (strlen (username) + 1 + strlen (password) + 1);
if (NULL == expected)
return 0;
strcpy (expected, username);
strcat (expected, ":");
strcat (expected, password);
expected_b64 = string_to_base64 (expected);
if (NULL == expected_b64)
return 0;
strcpy (expected, strbase);
authenticated =
(strcmp (headervalue + strlen (strbase), expected_b64) == 0);
free (expected_b64);
return authenticated;
}
int
secret_page (struct MHD_Connection *connection)
{
int ret;
struct MHD_Response *response;
const char *page = "<html><body>A secret.</body></html>";
response =
MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
MHD_NO);
if (!response)
return MHD_NO;
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
MHD_destroy_response (response);
return ret;
}
int
answer_to_connection (void *cls, struct MHD_Connection *connection,
const char *url, const char *method,
const char *version, const char *upload_data,
unsigned int *upload_data_size, void **con_cls)
{
if (0 != strcmp (method, "GET"))
return MHD_NO;
if (NULL == *con_cls)
{
*con_cls = connection;
return MHD_YES;
}
if (!is_authenticated (connection, USER, PASSWORD))
return ask_for_authentication (connection, REALM);
return secret_page (connection);
}
int
main ()
{
struct MHD_Daemon *daemon;
char *key_pem;
char *cert_pem;
key_pem = load_file (SERVERKEYFILE);
cert_pem = load_file (SERVERCERTFILE);
if ((key_pem == NULL) || (cert_pem == NULL))
{
printf ("The key/certificate files could not be read.\n");
return 1;
}
daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL, PORT, NULL, NULL,
&answer_to_connection, NULL,
MHD_OPTION_HTTPS_MEM_KEY, key_pem, MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
MHD_OPTION_END);
if (NULL == daemon)
{
printf ("%s\n", cert_pem);
free (key_pem);
free (cert_pem);
return 1;
}
getchar ();
MHD_stop_daemon (daemon);
free (key_pem);
free (cert_pem);
return 0;
}
char *
string_to_base64 (const char *message)
{
const char *lookup =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
unsigned long l;
int i;
char *tmp;
size_t length = strlen (message);
tmp = malloc (length * 2);
if (NULL == tmp)
return tmp;
tmp[0] = 0;
for (i = 0; i < length; i += 3)
{
l = (((unsigned long) message[i]) << 16)
| (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0)
| (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0);
strncat (tmp, &lookup[(l >> 18) & 0x3F], 1);
strncat (tmp, &lookup[(l >> 12) & 0x3F], 1);
if (i + 1 < length)
strncat (tmp, &lookup[(l >> 6) & 0x3F], 1);
if (i + 2 < length)
strncat (tmp, &lookup[l & 0x3F], 1);
}
if (length % 3)
strncat (tmp, "===", 3 - length % 3);
return tmp;
}
+11
View File
@@ -39,6 +39,7 @@ Free Documentation License".
* Supporting basic authentication::
* Processing POST data::
* Improved processing of POST data::
* Adding a layer of security::
* Bibliography::
* License text::
* Example programs::
@@ -72,6 +73,10 @@ Free Documentation License".
@chapter Improved processing of POST data
@include chapters/largerpost.inc
@node Adding a layer of security
@chapter Adding a layer of security
@include chapters/tlsauthentication.inc
@node Bibliography
@appendix Bibliography
@include chapters/bibliography.inc
@@ -89,6 +94,7 @@ Free Documentation License".
* basicauthentication.c::
* simplepost.c::
* largepost.c::
* tlsauthentication.c::
@end menu
@node hellobrowser.c
@@ -127,5 +133,10 @@ Free Documentation License".
@verbatiminclude examples/largepost.c
@end smalldisplay
@node tlsauthentication.c
@section tlsauthentication.c
@smalldisplay
@verbatiminclude examples/tlsauthentication.c
@end smalldisplay
@bye