updating docs

This commit is contained in:
Christian Grothoff
2011-01-01 13:47:44 +00:00
parent 964326eb53
commit 3af8f4ce64
6 changed files with 113 additions and 253 deletions
+3
View File
@@ -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
+10 -10
View File
@@ -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"])
+47 -97
View File
@@ -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.
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.
We let an extra function function do this.
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 = "<html><body>Go away.</body></html>";
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 = "<html><body>A secret.</body></html>";
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.
+16 -2
View File
@@ -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 <gnutls/gnutls.h>
+36 -143
View File
@@ -3,108 +3,12 @@
#include <sys/socket.h>
#include <microhttpd.h>
#include <time.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#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 = "<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;
}
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 = "<html><body>Go away.</body></html>";
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 = "<html><body>A secret.</body></html>";
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;
}
+1 -1
View File
@@ -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".