libmicrohttpd

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

commit 2132000306890e42d48ff535d786d382d14c3985
parent 1a951599f1ece8d91a68a9c9d80188cca241b2e7
Author: lv-426 <oxcafebaby@yahoo.com>
Date:   Mon,  2 Jun 2008 02:11:51 +0000

added X.509 parameters to the daemon struct
added https daemon creation functionality
https file server example [overriding existing echo server]

Diffstat:
Msrc/daemon/Makefile.am | 10+++++++---
Msrc/daemon/daemon.c | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/daemon/internal.h | 20++++++++++++++++++++
Msrc/examples/Makefile.am | 13+++++++++++++
Msrc/examples/https_server_example.c | 261++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Msrc/include/microhttpd.h | 8++++++++
6 files changed, 339 insertions(+), 70 deletions(-)

diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am @@ -11,8 +11,6 @@ EXTRA_DIST = SYMBOLS lib_LTLIBRARIES = \ libmicrohttpd.la -libmicrohttpd_la_LDFLAGS = \ - -export-dynamic -version-info 4:3:0 $(retaincommand) libmicrohttpd_la_SOURCES = \ connection.c connection.h \ reason_phrase.c reason_phrase.h \ @@ -21,7 +19,13 @@ libmicrohttpd_la_SOURCES = \ memorypool.c memorypool.h \ plibc.h \ postprocessor.c \ - response.c response.h + response.c response.h +libmicrohttpd_la_LDFLAGS = \ + -export-dynamic -version-info 4:3:0 $(retaincommand) \ + -L$(GNUTLS_LIB_PATH) \ + -lgnutls +libmicrohttpd_la_CPPFLAGS = \ + $(GNUTLS_CPPFLAGS) check_PROGRAMS = \ postprocessor_test \ diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c @@ -29,6 +29,7 @@ #include "response.h" #include "connection.h" #include "memorypool.h" +#include <gnutls/gnutls.h> /** * Default connection limit. @@ -52,6 +53,12 @@ */ #define DEBUG_CONNECT MHD_NO +// TODO rm +/* HTTPS file path limit, leaving room for file name */ +#define MHD_PATH_LEN 240 + +int MHDS_init (struct MHD_Daemon *daemon); + /** * Obtain the select sets for this daemon. * @@ -174,6 +181,8 @@ MHDS_handle_connection (void *data) if (con == NULL) abort (); + // TODO add connection time out code + /* forward call to handler */ con->daemon->default_handler (NULL, con, NULL, NULL, NULL, NULL, NULL, NULL); @@ -690,6 +699,13 @@ MHD_start_daemon (unsigned int options, retVal->pool_size = MHD_POOL_SIZE_DEFAULT; retVal->connection_timeout = 0; /* no timeout */ + /* set server default document root path */ + getcwd (retVal->doc_root, MHD_PATH_LEN); + + /* initialize ssl path parameters to the local path */ + strcpy (retVal->https_cert_path, "cert.pem"); + strcpy (retVal->https_key_path, "key.pem"); + /* initializes the argument pointer variable */ va_start (ap, dh_cls); @@ -717,6 +733,22 @@ MHD_start_daemon (unsigned int options, case MHD_OPTION_PER_IP_CONNECTION_LIMIT: retVal->per_ip_connection_limit = va_arg (ap, unsigned int); break; + case MHD_OPTION_DOC_ROOT: + strncpy (retVal->doc_root, va_arg (ap, char *), MHD_PATH_LEN); + break; + case MHD_OPTION_HTTPS_KEY_PATH: + strncpy (retVal->https_key_path, va_arg (ap, char *), MHD_PATH_LEN); + strcat (retVal->https_key_path, DIR_SEPARATOR_STR); + strcat (retVal->https_key_path, "key.pem"); + break; + case MHD_OPTION_HTTPS_CERT_PATH: + + strncpy (retVal->https_cert_path, + va_arg (ap, char *), MHD_PATH_LEN); + strcat (retVal->https_cert_path, DIR_SEPARATOR_STR); + strcat (retVal->https_cert_path, "cert.pem"); + break; + default: #if HAVE_MESSAGES fprintf (stderr, @@ -725,6 +757,29 @@ MHD_start_daemon (unsigned int options, abort (); } } + + /* initialize HTTPS daemon certificate aspects */ + if (options & MHD_USE_SSL) + { + /* test for private key & certificate file exsitance */ + FILE *cert_file = fopen (retVal->https_cert_path, "r"); + FILE *key_file = fopen (retVal->https_key_path, "r"); + if (key_file == NULL || cert_file == NULL) + { + printf ("missing cert files"); +#if HAVE_MESSAGES + MHD_DLOG (retVal, "Missing X.509 key or certificate file\n"); +#endif + free (retVal); + CLOSE (socket_fd); + return NULL; + } + + fclose (cert_file); + fclose (key_file); + MHDS_init (retVal); + } + va_end (ap); if (((0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || (0 != (options & @@ -793,9 +848,51 @@ MHD_stop_daemon (struct MHD_Daemon *daemon) } MHD_cleanup_connections (daemon); } + + /* TLS clean up */ + if (daemon->options & MHD_USE_SSL) + { + gnutls_priority_deinit (daemon->priority_cache); + gnutls_global_deinit (); + } + free (daemon); } +int +MHDS_init (struct MHD_Daemon *daemon) +{ + gnutls_global_init (); + /* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */ + gnutls_dh_params_init (&daemon->dh_params); + gnutls_dh_params_generate2 (daemon->dh_params, DH_BITS); + + // TODO make room for cipher settings adjustment + gnutls_priority_init (&daemon->priority_cache, + "NORMAL:+AES-256-CBC:+RSA:+SHA1:+COMP-NULL", NULL); + + /* setup server certificate */ + gnutls_certificate_allocate_credentials (&daemon->x509_cret); + + // TODO remove if unused + /* add trusted CAs to certificate */ + // gnutls_certificate_set_x509_trust_file(x509_cret, CAFILE,GNUTLS_X509_FMT_PEM); + + /* add Certificate revocation list to certificate */ + //gnutls_certificate_set_x509_crl_file(x509_cret, CRLFILE, GNUTLS_X509_FMT_PEM); + + /* sets a certificate private key pair */ + gnutls_certificate_set_x509_key_file (daemon->x509_cret, + daemon->https_cert_path, + daemon->https_key_path, + GNUTLS_X509_FMT_PEM); + + gnutls_certificate_set_dh_params (daemon->x509_cret, daemon->dh_params); + + // TODO address error case return value + return 0; +} + #ifndef WINDOWS static struct sigaction sig; diff --git a/src/daemon/internal.h b/src/daemon/internal.h @@ -35,6 +35,7 @@ #include <errno.h> #include <fcntl.h> #include <signal.h> +#include <gnutls/gnutls.h> #include "config.h" #include "plibc.h" @@ -58,6 +59,9 @@ */ #define MHD_BUF_INC_SIZE 2048 +/* TLS Diffie-Hellman parameter */ +#define DH_BITS 1024 + #if HAVE_MESSAGES /** * fprintf-like helper function for logging debug @@ -606,6 +610,22 @@ struct MHD_Daemon */ unsigned short port; + /* server credintials */ + gnutls_certificate_credentials_t x509_cret; + + /* cipher priority cache */ + gnutls_priority_t priority_cache; + + /* Diffie-Hellman parameters */ + gnutls_dh_params_t dh_params; + + // TODO consider switching to variadic length paths + /* server root path used while serving http pages */ + char doc_root[255]; + + char https_key_path[255]; + + char https_cert_path[255]; }; #endif diff --git a/src/examples/Makefile.am b/src/examples/Makefile.am @@ -6,6 +6,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src/include noinst_PROGRAMS = \ https_server_example \ +https_echo_server_example \ https_echo_client_example \ minimal_example \ querystring_example \ @@ -43,6 +44,18 @@ https_server_example_LDADD = \ https_server_example_LDFLAGS = \ -L$(GNUTLS_LIB_PATH) \ -lgnutls + +https_echo_server_example_CPPFLAGS = \ + $(GNUTLS_CPPFLAGS) \ + -I$(top_srcdir)/src/daemon \ + -I$(top_srcdir)/src/include +https_echo_server_example_SOURCES = \ + https_echo_server_example.c +https_echo_server_example_LDADD = \ + $(top_builddir)/src/daemon/libmicrohttpd.la +https_echo_server_example_LDFLAGS = \ + -L$(GNUTLS_LIB_PATH) \ + -lgnutls https_echo_client_example_CPPFLAGS = \ $(GNUTLS_CPPFLAGS) \ diff --git a/src/examples/https_server_example.c b/src/examples/https_server_example.c @@ -20,7 +20,17 @@ /** * @file https_server_example.c - * @brief a simple echo server using TLS. echo input from client until 'exit' message is received. + * @brief a simple https file server using TLS. + * + * This example assumes the existence of a private key file named "key.pem" + * and a server certificate file named "cert.pem". File path for these should be + * provided as command-line arguments. 'certtool' may be used to generate these if + * missing. + * + * Access server with your browser of choice or with curl : + * + * curl --insecure --tlsv1 --ciphers AES256-SHA <url> + * * @author LV-426 */ @@ -35,64 +45,76 @@ #include <string.h> #include <stdio.h> #include <gnutls/gnutls.h> +#include <gcrypt.h> -#define DH_BITS 1024 -#define MAX_BUF 1024 -/* server credintials */ -gnutls_anon_server_credentials_t anoncred; +#define BUF_SIZE 1024 +#define MAX_URL_LEN 255 -/* server Diffie-Hellman parameters */ -static gnutls_dh_params_t dh_params; +#define KEYFILE "key.pem" +#define CERTFILE "cert.pem" +// TODO remove if unused +#define CAFILE "ca.pem" +#define CRLFILE "crl.pem" -/* Generate Diffie Hellman parameters - for use with DHE kx algorithms. */ -static int -generate_dh_params (void) -{ - - gnutls_dh_params_init (&dh_params); - gnutls_dh_params_generate2 (dh_params, DH_BITS); - return 0; -} +#define PAGE_NOT_FOUND "<html><head><title>File not found</title></head><body>File not found</body></html>" gnutls_session_t -initialize_tls_session (void) +initialize_tls_session (struct MHD_Connection *connection) { gnutls_session_t session; gnutls_init (&session, GNUTLS_SERVER); - gnutls_priority_set_direct (session, "NORMAL:+ANON-DH", NULL); + /* sets cipher priorities */ + gnutls_priority_set (session, connection->daemon->priority_cache); - gnutls_credentials_set (session, GNUTLS_CRD_ANON, anoncred); - - gnutls_dh_set_prime_bits (session, DH_BITS); + /* set needed credentials for certificate authentication. */ + gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, + connection->daemon->x509_cret); return session; } -/* Accept Policy Callback */ static int -TLS_echo (void *cls, - struct MHD_Connection *connection, - const char *url, - const char *method, - const char *upload_data, - const char *version, unsigned int *upload_data_size, void **ptr) +file_reader (void *cls, size_t pos, char *buf, int max) { + FILE *file = cls; + + fseek (file, pos, SEEK_SET); + return fread (buf, 1, max, file); +} + +/* HTTPS access handler call back */ +static int +https_ahc (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *upload_data, + const char *version, unsigned int *upload_data_size, void **ptr) +{ + /* loopback HTTP socket */ + int loopback_sd, err; + ssize_t ret; + struct sockaddr_in servaddr4; + const struct sockaddr *servaddr; + struct sockaddr_in loopback_sa; + socklen_t addrlen; + gnutls_session_t session; static int aptr; struct MHD_Response *response; - char buffer[MAX_BUF + 1]; - int ret; + char buffer[BUF_SIZE]; printf ("accepted connection from %d\n", connection->addr->sin_addr); - session = initialize_tls_session (); + session = initialize_tls_session (connection); gnutls_transport_set_ptr (session, connection->socket_fd); ret = gnutls_handshake (session); + if (ret < 0) { /* set connection as closed */ @@ -106,77 +128,182 @@ TLS_echo (void *cls, printf ("TLS Handshake completed\n"); connection->state = MHDS_HANDSHAKE_COMPLETE; - /* simple echo loop. message encryption/decryption is acheived through 'gnutls_record_send' - * & gnutls_record_recv calls. */ + /* initialize loopback socket */ + loopback_sd = socket (AF_INET, SOCK_STREAM, 0); + memset (&loopback_sa, '\0', sizeof (loopback_sa)); + loopback_sa.sin_family = AF_INET; + + // TODO solve magic number issue - the http's daemons port must be shared with the https daemon - rosolve data sharing point + loopback_sa.sin_port = htons (50000); + inet_pton (AF_INET, "127.0.0.1", &loopback_sa.sin_addr); + + /* connect loopback socket */ + err = connect (loopback_sd, (struct sockaddr *) &loopback_sa, + sizeof (loopback_sa)); + if (err < 0) + { + // TODO err handle + fprintf (stderr, "Error : failed to create TLS loopback socket\n"); + exit (1); + } + + /* + * This loop pipes data received through the TLS tunnel into the loopback connection. + * message encryption/decryption is acheived via 'gnutls_record_send' & gnutls_record_recv calls. + */ + memset (buffer, 0, BUF_SIZE); + if (gnutls_record_recv (session, buffer, BUF_SIZE) < 0) + { + fprintf (stderr, "\n*** Received corrupted " + "data(%d). Closing the connection.\n\n", ret); + connection->socket_fd = -1; + gnutls_deinit (session); + return MHD_NO; + } + + if (write (loopback_sd, buffer, BUF_SIZE) < 0) + { + printf ("failed to write to TLS loopback socket\n"); + connection->socket_fd = -1; + gnutls_deinit (session); + return MHD_NO; + } + for (;;) { - memset (buffer, 0, MAX_BUF + 1); - ret = gnutls_record_recv (session, buffer, MAX_BUF); + memset (buffer, 0, BUF_SIZE); + + ret = read (loopback_sd, buffer, BUF_SIZE); if (ret < 0) { - fprintf (stderr, "\n*** Received corrupted " - "data(%d). Closing the connection.\n\n", ret); + printf ("failed to read from TLS loopback socket\n"); break; } - else if (ret >= 0) + + if (ret == 0) { - if (strcmp (buffer, "exit") == 0) - { - printf ("\n- Peer has closed the GNUTLS connection\n"); - break; - } - else - { - /* echo data back to the client */ - gnutls_record_send (session, buffer, strlen (buffer)); - } + break; } - } - printf ("\n"); + /* echo data back to the client */ + ret = gnutls_record_send (session, buffer, ret); + if (ret < 0) + { + printf ("failed to write to TLS socket\n"); + break; + } + } /* mark connection as closed */ connection->socket_fd = -1; - gnutls_deinit (session); + return MHD_YES; +} + +/* HTTP access handler call back */ +static int +http_ahc (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *upload_data, + const char *version, unsigned int *upload_data_size, void **ptr) +{ + static int aptr; + static char full_url[MAX_URL_LEN]; + struct MHD_Response *response; + int ret; + FILE *file; + struct stat buf; + + if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) + return MHD_NO; /* unexpected method */ + if (&aptr != *ptr) + { + /* do never respond on first call */ + *ptr = &aptr; + return MHD_YES; + } + *ptr = NULL; /* reset when done */ + + /* assemble full url */ + strcpy (full_url, connection->daemon->doc_root); + strncat (full_url, url, + MAX_URL_LEN - strlen (connection->daemon->doc_root) - 1); + + file = fopen (full_url, "r"); + if (file == NULL) + { + response = MHD_create_response_from_data (strlen (PAGE_NOT_FOUND), + (void *) PAGE_NOT_FOUND, + MHD_NO, MHD_NO); + ret = MHD_queue_response (connection, MHD_HTTP_NOT_FOUND, response); + MHD_destroy_response (response); + } + else + { + stat (&url[1], &buf); + response = MHD_create_response_from_callback (buf.st_size, 32 * 1024, /* 32k PAGE_NOT_FOUND size */ + &file_reader, file, + (MHD_ContentReaderFreeCallback) + & fclose); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + MHD_destroy_response (response); + } return ret; } int main (int argc, char *const *argv) { - struct MHD_Daemon *daemon; + char keyfile[255] = KEYFILE; + char certfile[255] = CERTFILE; + struct MHD_Daemon *HTTP_daemon; struct MHD_Daemon *TLS_daemon; - /* look for HTTPS port argument */ - if (argc < 4) + /* look for HTTPS arguments */ + if (argc < 5) { - printf ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT\n", argv[0]); + printf + ("Usage : %s HTTP-PORT SECONDS-TO-RUN HTTPS-PORT X.509_FILE_PATH\n", + argv[0]); return 1; } - gnutls_global_init (); - - gnutls_anon_allocate_server_credentials (&anoncred); + // TODO check if this is truly necessary - disallow usage of the blocking /dev/random */ + // gcry_control(GCRYCTL_ENABLE_QUICK_RANDOM, 0); - generate_dh_params (); + HTTP_daemon = + MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG, + atoi (argv[1]), NULL, NULL, &http_ahc, MHD_OPTION_END); - gnutls_anon_set_server_dh_params (anoncred, dh_params); + if (HTTP_daemon == NULL) + { + printf ("Error: failed to start HTTP_daemon"); + return 1; + } - TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION - | MHD_USE_DEBUG | MHD_USE_SSL, - atoi (argv[3]), NULL, NULL, &TLS_echo, NULL, + TLS_daemon = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION | MHD_USE_DEBUG + | MHD_USE_SSL, atoi (argv[3]), + NULL, + NULL, &https_ahc, + NULL, MHD_OPTION_CONNECTION_TIMEOUT, 256, + MHD_OPTION_HTTPS_KEY_PATH, argv[4], + MHD_OPTION_HTTPS_CERT_PATH, argv[4], MHD_OPTION_END); if (TLS_daemon == NULL) - return 1; + { + printf ("Error: failed to start TLS_daemon"); + return 1; + } + sleep (atoi (argv[2])); - MHD_stop_daemon (daemon); + MHD_stop_daemon (HTTP_daemon); - gnutls_anon_free_server_credentials (anoncred); + MHD_stop_daemon (TLS_daemon); - gnutls_global_deinit (); return 0; } diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -345,6 +345,14 @@ enum MHD_OPTION */ MHD_OPTION_PER_IP_CONNECTION_LIMIT = 5, + /* server root path used while serving http pages */ + MHD_OPTION_DOC_ROOT = 6, + + /* private key path used by the HTTPS daemon */ + MHD_OPTION_HTTPS_KEY_PATH = 7, + + /* certificate path used by the HTTPS daemon */ + MHD_OPTION_HTTPS_CERT_PATH = 8, }; /**