libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 8636c4b3084d678bf99f2ff0ee46d07727e07a8a
parent 8bee920d50ed850e0354b18c6b8cd4a993069620
Author: Sebastian Gerhardt <sebgerhardt@gmx.net>
Date:   Sat, 11 Oct 2008 17:55:14 +0000

Tutorial: New chapter for SSL/TLS


Diffstat:
Adoc/chapters/tlsauthentication.inc | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adoc/examples/tlsauthentication.c | 253+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdoc/tutorial.texi | 11+++++++++++
3 files changed, 394 insertions(+), 0 deletions(-)

diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc @@ -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 + + diff --git a/doc/examples/tlsauthentication.c b/doc/examples/tlsauthentication.c @@ -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; +} + diff --git a/doc/tutorial.texi b/doc/tutorial.texi @@ -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