From 3af8f4ce646d29cd5942a1e3dda3b7ed03a82af6 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sat, 1 Jan 2011 13:47:44 +0000 Subject: updating docs --- ChangeLog | 3 + configure.ac | 20 ++-- doc/chapters/basicauthentication.inc | 148 ++++++++++------------------- doc/chapters/tlsauthentication.inc | 18 +++- doc/examples/basicauthentication.c | 179 +++++++---------------------------- src/include/microhttpd.h | 2 +- 6 files changed, 115 insertions(+), 255 deletions(-) diff --git a/ChangeLog b/ChangeLog index caa00fd0..38854d96 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Sun Dec 26 00:02:15 CET 2010 + Releasing libmicrohttpd 0.9.4. -CG + Sat Dec 25 21:57:14 CET 2010 Adding support for basic authentication. Documented how to obtain client SSL certificates in tutorial. -MS diff --git a/configure.ac b/configure.ac index b2a53fed..204d32ac 100644 --- a/configure.ac +++ b/configure.ac @@ -21,15 +21,15 @@ # # AC_PREREQ(2.57) -AC_INIT([libmicrohttpd], [0.9.3],[libmicrohttpd@gnu.org]) -AM_INIT_AUTOMAKE([libmicrohttpd], [0.9.3]) +AC_INIT([libmicrohttpd], [0.9.4],[libmicrohttpd@gnu.org]) +AM_INIT_AUTOMAKE([libmicrohttpd], [0.9.4]) AM_CONFIG_HEADER([MHD_config.h]) AC_CONFIG_MACRO_DIR([m4]) AH_TOP([#define _GNU_SOURCE 1]) -LIB_VERSION_CURRENT=11 +LIB_VERSION_CURRENT=12 LIB_VERSION_REVISION=0 -LIB_VERSION_AGE=1 +LIB_VERSION_AGE=2 AC_SUBST(LIB_VERSION_CURRENT) AC_SUBST(LIB_VERSION_REVISION) AC_SUBST(LIB_VERSION_AGE) @@ -201,7 +201,7 @@ AC_MSG_CHECKING(whether to use libcurl for testing) AC_ARG_ENABLE([curl], [AS_HELP_STRING([--disable-curl],[disable cURL based testcases])], [enable_curl=${enableval}], - [enable_curl=no]) + [enable_curl=yes]) AC_MSG_RESULT($enable_curl) curl=0 if test "$enable_curl" = "yes" @@ -230,7 +230,7 @@ AC_ARG_ENABLE([messages], [AS_HELP_STRING([--disable-messages], [disable MHD error messages])], [enable_messages=${enableval}], - [enable_messages=no]) + [enable_messages=yes]) AC_MSG_RESULT($enable_messages) if test "$enable_messages" = "yes" then @@ -246,7 +246,7 @@ AC_ARG_ENABLE([postprocessor], [AS_HELP_STRING([--disable-postprocessor], [disable MHD PostProcessor functionality])], [enable_postprocessor=${enableval}], - [enable_postprocessor=no]) + [enable_postprocessor=yes]) AC_MSG_RESULT($disable_postprocessor) AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$enable_postprocessor != xno) @@ -305,7 +305,7 @@ AC_ARG_ENABLE([https], [AS_HELP_STRING([--disable-https], [disable HTTPS support])], [enable_https=${enableval}], - [enable_https=no]) + [enable_https=yes]) if test "$enable_https" = "yes" then if test "$gcrypt" = "true" -a "$gnutls" = "true" @@ -330,7 +330,7 @@ AC_ARG_ENABLE([dauth], AS_HELP_STRING([--disable-dauth], [disable HTTP basic and digest Auth support]), [enable_dauth=${enableval}], - [enable_dauth=no]) + [enable_dauth=yes]) if test "$enable_dauth" = "yes" then @@ -360,7 +360,7 @@ AC_ARG_ENABLE([coverage], AS_HELP_STRING([--enable-coverage], [compile the library with code coverage support]), [use_gcov=${enableval}], - [use_gcov=yes]) + [use_gcov=no]) AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"]) diff --git a/doc/chapters/basicauthentication.inc b/doc/chapters/basicauthentication.inc index 2b68fc4c..8c7c241c 100644 --- a/doc/chapters/basicauthentication.inc +++ b/doc/chapters/basicauthentication.inc @@ -69,112 +69,62 @@ Let us assume we had only files not intended to be handed out without the correc so every "GET" request will be challenged. @emph{RFC 2617} describes how the server shall ask for authentication by adding a @emph{WWW-Authenticate} response header with the name of the @emph{realm} protected. - -We let an extra function function do this. +MHD can generate and queue such a failure response for you using +the @code{MHD_queue_basic_auth_fail_response} API. The only thing you need to do +is construct a response with the error page to be shown to the user +if he aborts basic authentication. But first, you should check if the +proper credentials were already supplied using the +@code{MHD_basic_auth_get_username_password} call. + +Your code would then look like this: @verbatim -static int -ask_for_authentication (struct MHD_Connection *connection, const char *realm) +static int +answer_to_connection (void *cls, struct MHD_Connection *connection, + const char *url, const char *method, + const char *version, const char *upload_data, + size_t *upload_data_size, void **con_cls) { - int ret; + char *user; + char *pass; + int fail; 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); + + if (0 != strcmp (method, MHD_HTTP_METHOD_GET)) + return MHD_NO; + if (NULL == *con_cls) + { + *con_cls = connection; + return MHD_YES; + } + pass = NULL; + user = MHD_basic_auth_get_username_password (connection, &pass); + fail = ( (user == NULL) || + (0 != strcmp (user, "root")) || + (0 != strcmp (pass, "pa$$w0rd") ) ); + if (user != NULL) free (user); + if (pass != NULL) free (pass); + if (fail) + { + const char *page = "Go away."; + response = + MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO, + MHD_NO); + ret = MHD_queue_basic_auth_fail_response (connection, + "my realm", + response); + } + else + { + const char *page = "A secret."; + response = + MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO, + MHD_NO); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + } MHD_destroy_response (response); return ret; } @end verbatim -@noindent - -@code{#define} the realm name according to your own taste, e.g. "Maintenance" or "Area51" but -it will need to have extra quotes. - -Since the client may send the authentication right away, it would be wrong to ask for -it without checking the request's header--where the authentication is expected to be found. - -@heading Authentication in detail -Checking @emph{RFC 2617} again, we find that the client will pack the username and password, by -whatever means he might have obtained them, in a line separated by a colon---and then encodes -them to @emph{Base64}. The actual implementation of this encoding are not within the scope of -this tutorial although a working function is included in the complete source file of the example. - -An unencoded word describing the authentication method (here "Basic") will precede the code -and the resulting line is the value of a request header of the type "Authorization". - -This header line thus is of interest to the function checking a connection for a given username/password: -@verbatim -static int -is_authenticated (struct MHD_Connection *connection, - const char *username, const char *password) -{ - const char *headervalue; - ... - - headervalue = MHD_lookup_connection_value (connection, MHD_HEADER_KIND, - "Authorization"); - if (NULL == headervalue) return 0; -@end verbatim -@noindent - -where, firstly, the authentication method will be checked. -@verbatim -const char *strbase = "Basic "; -... -if (0 != strncmp (headervalue, strbase, strlen (strbase))) return 0; -@end verbatim -@noindent - -Of course, we could decode the passed credentials in the next step and compare them directly to -the given strings. But as this would involve string parsing, which is more complicated then string -composing, it is done the other way around---the clear text credentials will be encoded to @emph{Base64} -and then compared against the headerline. The authentication method string will be left out here as -it has been checked already at this point. -@verbatim - char *expected_b64, *expected; - int authenticated; - - ... - 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; -} -@end verbatim -@noindent - -These two functions---together with a response function in case of positive authentication doing little -new---allow the rest of the callback function to be rather short. -@verbatim - if (!is_authenticated (connection, USER, PASSWORD)) - return ask_for_authentication (connection, REALM); - - return secret_page (connection); -} -@end verbatim -@noindent See the @code{examples} directory for the complete example file. diff --git a/doc/chapters/tlsauthentication.inc b/doc/chapters/tlsauthentication.inc index 4f9c4443..278a3ba5 100644 --- a/doc/chapters/tlsauthentication.inc +++ b/doc/chapters/tlsauthentication.inc @@ -135,8 +135,22 @@ both of uncritically @emph{HTTP} parts and secured @emph{HTTPS}. You can also use MHD to authenticate the client via SSL/TLS certificates (as an alternative to using the password-based Basic or Digest authentication). To do this, you will need to link your application against @emph{gnutls}. -For this, you first need to obtain the raw GnuTLS session handle from -@emph{MHD} using @code{MHD_get_connection_info}. +Next, when you start the MHD daemon, you must specify the root CA that you're +willing to trust: +@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_HTTPS_MEM_TRUST, root_ca_pem, + MHD_OPTION_END); +@end verbatim + +With this, you can then obtain client certificates for each session. +In order to obtain the identity of the client, you first need to +obtain the raw GnuTLS session handle from @emph{MHD} using +@code{MHD_get_connection_info}. @verbatim #include diff --git a/doc/examples/basicauthentication.c b/doc/examples/basicauthentication.c index 389242da..0b5ce62c 100644 --- a/doc/examples/basicauthentication.c +++ b/doc/examples/basicauthentication.c @@ -3,108 +3,12 @@ #include #include #include +#include +#include +#include #define PORT 8888 -#define REALM "\"Maintenance\"" -#define USER "a legitimate user" -#define PASSWORD "and his password" - - -char *string_to_base64 (const char *message); - - -static 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; -} - -static 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); - free (expected); - if (NULL == expected_b64) - return 0; - - authenticated = - (strcmp (headervalue + strlen (strbase), expected_b64) == 0); - - free (expected_b64); - return authenticated; -} - - -static int -secret_page (struct MHD_Connection *connection) -{ - int ret; - struct MHD_Response *response; - const char *page = "A secret."; - - 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; -} - static int answer_to_connection (void *cls, struct MHD_Connection *connection, @@ -112,6 +16,12 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { + char *user; + char *pass; + int fail; + int ret; + struct MHD_Response *response; + if (0 != strcmp (method, "GET")) return MHD_NO; if (NULL == *con_cls) @@ -119,11 +29,33 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, *con_cls = connection; return MHD_YES; } - - if (!is_authenticated (connection, USER, PASSWORD)) - return ask_for_authentication (connection, REALM); - - return secret_page (connection); + pass = NULL; + user = MHD_basic_auth_get_username_password (connection, &pass); + fail = ( (user == NULL) || + (0 != strcmp (user, "root")) || + (0 != strcmp (pass, "pa$$w0rd") ) ); + if (user != NULL) free (user); + if (pass != NULL) free (pass); + if (fail) + { + const char *page = "Go away."; + response = + MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO, + MHD_NO); + ret = MHD_queue_basic_auth_fail_response (connection, + "my realm", + response); + } + else + { + const char *page = "A secret."; + response = + MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO, + MHD_NO); + ret = MHD_queue_response (connection, MHD_HTTP_OK, response); + } + MHD_destroy_response (response); + return ret; } @@ -142,42 +74,3 @@ main () MHD_stop_daemon (daemon); 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/src/include/microhttpd.h b/src/include/microhttpd.h index 36961275..f123969b 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h @@ -106,7 +106,7 @@ extern "C" /** * Current version of the library. */ -#define MHD_VERSION 0x00090301 +#define MHD_VERSION 0x00090400 /** * MHD-internal return code for "YES". -- cgit v1.2.3