aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2011-01-01 13:47:44 +0000
committerChristian Grothoff <christian@grothoff.org>2011-01-01 13:47:44 +0000
commit3af8f4ce646d29cd5942a1e3dda3b7ed03a82af6 (patch)
treefe9ede8fc028e6b7ebd2c683be623214b365f1a7
parent964326eb5348b5acf7d1cff7fb2f3c0d44ff4716 (diff)
downloadlibmicrohttpd-3af8f4ce646d29cd5942a1e3dda3b7ed03a82af6.tar.gz
libmicrohttpd-3af8f4ce646d29cd5942a1e3dda3b7ed03a82af6.zip
updating docs
-rw-r--r--ChangeLog3
-rw-r--r--configure.ac20
-rw-r--r--doc/chapters/basicauthentication.inc148
-rw-r--r--doc/chapters/tlsauthentication.inc18
-rw-r--r--doc/examples/basicauthentication.c179
-rw-r--r--src/include/microhttpd.h2
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 @@
1Sun Dec 26 00:02:15 CET 2010
2 Releasing libmicrohttpd 0.9.4. -CG
3
1Sat Dec 25 21:57:14 CET 2010 4Sat Dec 25 21:57:14 CET 2010
2 Adding support for basic authentication. 5 Adding support for basic authentication.
3 Documented how to obtain client SSL certificates in tutorial. -MS 6 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 @@
21# 21#
22# 22#
23AC_PREREQ(2.57) 23AC_PREREQ(2.57)
24AC_INIT([libmicrohttpd], [0.9.3],[libmicrohttpd@gnu.org]) 24AC_INIT([libmicrohttpd], [0.9.4],[libmicrohttpd@gnu.org])
25AM_INIT_AUTOMAKE([libmicrohttpd], [0.9.3]) 25AM_INIT_AUTOMAKE([libmicrohttpd], [0.9.4])
26AM_CONFIG_HEADER([MHD_config.h]) 26AM_CONFIG_HEADER([MHD_config.h])
27AC_CONFIG_MACRO_DIR([m4]) 27AC_CONFIG_MACRO_DIR([m4])
28AH_TOP([#define _GNU_SOURCE 1]) 28AH_TOP([#define _GNU_SOURCE 1])
29 29
30LIB_VERSION_CURRENT=11 30LIB_VERSION_CURRENT=12
31LIB_VERSION_REVISION=0 31LIB_VERSION_REVISION=0
32LIB_VERSION_AGE=1 32LIB_VERSION_AGE=2
33AC_SUBST(LIB_VERSION_CURRENT) 33AC_SUBST(LIB_VERSION_CURRENT)
34AC_SUBST(LIB_VERSION_REVISION) 34AC_SUBST(LIB_VERSION_REVISION)
35AC_SUBST(LIB_VERSION_AGE) 35AC_SUBST(LIB_VERSION_AGE)
@@ -201,7 +201,7 @@ AC_MSG_CHECKING(whether to use libcurl for testing)
201AC_ARG_ENABLE([curl], 201AC_ARG_ENABLE([curl],
202 [AS_HELP_STRING([--disable-curl],[disable cURL based testcases])], 202 [AS_HELP_STRING([--disable-curl],[disable cURL based testcases])],
203 [enable_curl=${enableval}], 203 [enable_curl=${enableval}],
204 [enable_curl=no]) 204 [enable_curl=yes])
205AC_MSG_RESULT($enable_curl) 205AC_MSG_RESULT($enable_curl)
206curl=0 206curl=0
207if test "$enable_curl" = "yes" 207if test "$enable_curl" = "yes"
@@ -230,7 +230,7 @@ AC_ARG_ENABLE([messages],
230 [AS_HELP_STRING([--disable-messages], 230 [AS_HELP_STRING([--disable-messages],
231 [disable MHD error messages])], 231 [disable MHD error messages])],
232 [enable_messages=${enableval}], 232 [enable_messages=${enableval}],
233 [enable_messages=no]) 233 [enable_messages=yes])
234AC_MSG_RESULT($enable_messages) 234AC_MSG_RESULT($enable_messages)
235if test "$enable_messages" = "yes" 235if test "$enable_messages" = "yes"
236then 236then
@@ -246,7 +246,7 @@ AC_ARG_ENABLE([postprocessor],
246 [AS_HELP_STRING([--disable-postprocessor], 246 [AS_HELP_STRING([--disable-postprocessor],
247 [disable MHD PostProcessor functionality])], 247 [disable MHD PostProcessor functionality])],
248 [enable_postprocessor=${enableval}], 248 [enable_postprocessor=${enableval}],
249 [enable_postprocessor=no]) 249 [enable_postprocessor=yes])
250AC_MSG_RESULT($disable_postprocessor) 250AC_MSG_RESULT($disable_postprocessor)
251AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$enable_postprocessor != xno) 251AM_CONDITIONAL([HAVE_POSTPROCESSOR],test x$enable_postprocessor != xno)
252 252
@@ -305,7 +305,7 @@ AC_ARG_ENABLE([https],
305 [AS_HELP_STRING([--disable-https], 305 [AS_HELP_STRING([--disable-https],
306 [disable HTTPS support])], 306 [disable HTTPS support])],
307 [enable_https=${enableval}], 307 [enable_https=${enableval}],
308 [enable_https=no]) 308 [enable_https=yes])
309if test "$enable_https" = "yes" 309if test "$enable_https" = "yes"
310then 310then
311 if test "$gcrypt" = "true" -a "$gnutls" = "true" 311 if test "$gcrypt" = "true" -a "$gnutls" = "true"
@@ -330,7 +330,7 @@ AC_ARG_ENABLE([dauth],
330 AS_HELP_STRING([--disable-dauth], 330 AS_HELP_STRING([--disable-dauth],
331 [disable HTTP basic and digest Auth support]), 331 [disable HTTP basic and digest Auth support]),
332 [enable_dauth=${enableval}], 332 [enable_dauth=${enableval}],
333 [enable_dauth=no]) 333 [enable_dauth=yes])
334 334
335if test "$enable_dauth" = "yes" 335if test "$enable_dauth" = "yes"
336then 336then
@@ -360,7 +360,7 @@ AC_ARG_ENABLE([coverage],
360 AS_HELP_STRING([--enable-coverage], 360 AS_HELP_STRING([--enable-coverage],
361 [compile the library with code coverage support]), 361 [compile the library with code coverage support]),
362 [use_gcov=${enableval}], 362 [use_gcov=${enableval}],
363 [use_gcov=yes]) 363 [use_gcov=no])
364AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"]) 364AM_CONDITIONAL([USE_COVERAGE], [test "x$use_gcov" = "xyes"])
365 365
366 366
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
69so every "GET" request will be challenged. 69so every "GET" request will be challenged.
70@emph{RFC 2617} describes how the server shall ask for authentication by adding a 70@emph{RFC 2617} describes how the server shall ask for authentication by adding a
71@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected. 71@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected.
72 72MHD can generate and queue such a failure response for you using
73We let an extra function function do this. 73the @code{MHD_queue_basic_auth_fail_response} API. The only thing you need to do
74is construct a response with the error page to be shown to the user
75if he aborts basic authentication. But first, you should check if the
76proper credentials were already supplied using the
77@code{MHD_basic_auth_get_username_password} call.
78
79Your code would then look like this:
74@verbatim 80@verbatim
75static int 81static int
76ask_for_authentication (struct MHD_Connection *connection, const char *realm) 82answer_to_connection (void *cls, struct MHD_Connection *connection,
83 const char *url, const char *method,
84 const char *version, const char *upload_data,
85 size_t *upload_data_size, void **con_cls)
77{ 86{
78 int ret; 87 char *user;
88 char *pass;
89 int fail;
79 struct MHD_Response *response; 90 struct MHD_Response *response;
80 char *headervalue; 91
81 const char *strbase = "Basic realm="; 92 if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
82 93 return MHD_NO;
83 response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO); 94 if (NULL == *con_cls)
84 if (!response) return MHD_NO; 95 {
85 96 *con_cls = connection;
86 headervalue = malloc (strlen (strbase) + strlen (realm) + 1); 97 return MHD_YES;
87 if (!headervalue) return MHD_NO; 98 }
88 99 pass = NULL;
89 strcpy (headervalue, strbase); 100 user = MHD_basic_auth_get_username_password (connection, &pass);
90 strcat (headervalue, realm); 101 fail = ( (user == NULL) ||
91 102 (0 != strcmp (user, "root")) ||
92 ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue); 103 (0 != strcmp (pass, "pa$$w0rd") ) );
93 free (headervalue); 104 if (user != NULL) free (user);
94 if (!ret) {MHD_destroy_response (response); return MHD_NO;} 105 if (pass != NULL) free (pass);
95 106 if (fail)
96 ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); 107 {
108 const char *page = "<html><body>Go away.</body></html>";
109 response =
110 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
111 MHD_NO);
112 ret = MHD_queue_basic_auth_fail_response (connection,
113 "my realm",
114 response);
115 }
116 else
117 {
118 const char *page = "<html><body>A secret.</body></html>";
119 response =
120 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
121 MHD_NO);
122 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
123 }
97 MHD_destroy_response (response); 124 MHD_destroy_response (response);
98 return ret; 125 return ret;
99} 126}
100@end verbatim 127@end verbatim
101@noindent
102
103@code{#define} the realm name according to your own taste, e.g. "Maintenance" or "Area51" but
104it will need to have extra quotes.
105
106Since the client may send the authentication right away, it would be wrong to ask for
107it without checking the request's header--where the authentication is expected to be found.
108
109@heading Authentication in detail
110Checking @emph{RFC 2617} again, we find that the client will pack the username and password, by
111whatever means he might have obtained them, in a line separated by a colon---and then encodes
112them to @emph{Base64}. The actual implementation of this encoding are not within the scope of
113this tutorial although a working function is included in the complete source file of the example.
114
115An unencoded word describing the authentication method (here "Basic") will precede the code
116and the resulting line is the value of a request header of the type "Authorization".
117
118This header line thus is of interest to the function checking a connection for a given username/password:
119@verbatim
120static int
121is_authenticated (struct MHD_Connection *connection,
122 const char *username, const char *password)
123{
124 const char *headervalue;
125 ...
126
127 headervalue = MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
128 "Authorization");
129 if (NULL == headervalue) return 0;
130@end verbatim
131@noindent
132
133where, firstly, the authentication method will be checked.
134@verbatim
135const char *strbase = "Basic ";
136...
137if (0 != strncmp (headervalue, strbase, strlen (strbase))) return 0;
138@end verbatim
139@noindent
140
141Of course, we could decode the passed credentials in the next step and compare them directly to
142the given strings. But as this would involve string parsing, which is more complicated then string
143composing, it is done the other way around---the clear text credentials will be encoded to @emph{Base64}
144and then compared against the headerline. The authentication method string will be left out here as
145it has been checked already at this point.
146@verbatim
147 char *expected_b64, *expected;
148 int authenticated;
149
150 ...
151 strcpy (expected, username);
152 strcat (expected, ":");
153 strcat (expected, password);
154
155 expected_b64 = string_to_base64 (expected);
156 if (NULL == expected_b64) return 0;
157
158 strcpy (expected, strbase);
159 authenticated = (strcmp (headervalue + strlen (strbase), expected_b64) == 0);
160
161 free (expected_b64);
162
163 return authenticated;
164}
165@end verbatim
166@noindent
167
168These two functions---together with a response function in case of positive authentication doing little
169new---allow the rest of the callback function to be rather short.
170@verbatim
171 if (!is_authenticated (connection, USER, PASSWORD))
172 return ask_for_authentication (connection, REALM);
173
174 return secret_page (connection);
175}
176@end verbatim
177@noindent
178 128
179See the @code{examples} directory for the complete example file. 129See the @code{examples} directory for the complete example file.
180 130
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}.
135You can also use MHD to authenticate the client via SSL/TLS certificates 135You can also use MHD to authenticate the client via SSL/TLS certificates
136(as an alternative to using the password-based Basic or Digest authentication). 136(as an alternative to using the password-based Basic or Digest authentication).
137To do this, you will need to link your application against @emph{gnutls}. 137To do this, you will need to link your application against @emph{gnutls}.
138For this, you first need to obtain the raw GnuTLS session handle from 138Next, when you start the MHD daemon, you must specify the root CA that you're
139@emph{MHD} using @code{MHD_get_connection_info}. 139willing to trust:
140@verbatim
141 daemon = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_SSL,
142 PORT, NULL, NULL,
143 &answer_to_connection, NULL,
144 MHD_OPTION_HTTPS_MEM_KEY, key_pem,
145 MHD_OPTION_HTTPS_MEM_CERT, cert_pem,
146 MHD_OPTION_HTTPS_MEM_TRUST, root_ca_pem,
147 MHD_OPTION_END);
148@end verbatim
149
150With this, you can then obtain client certificates for each session.
151In order to obtain the identity of the client, you first need to
152obtain the raw GnuTLS session handle from @emph{MHD} using
153@code{MHD_get_connection_info}.
140 154
141@verbatim 155@verbatim
142#include <gnutls/gnutls.h> 156#include <gnutls/gnutls.h>
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 @@
3#include <sys/socket.h> 3#include <sys/socket.h>
4#include <microhttpd.h> 4#include <microhttpd.h>
5#include <time.h> 5#include <time.h>
6#include <string.h>
7#include <stdlib.h>
8#include <stdio.h>
6 9
7#define PORT 8888 10#define PORT 8888
8 11
9#define REALM "\"Maintenance\""
10#define USER "a legitimate user"
11#define PASSWORD "and his password"
12
13
14char *string_to_base64 (const char *message);
15
16
17static int
18ask_for_authentication (struct MHD_Connection *connection, const char *realm)
19{
20 int ret;
21 struct MHD_Response *response;
22 char *headervalue;
23 const char *strbase = "Basic realm=";
24
25 response = MHD_create_response_from_data (0, NULL, MHD_NO, MHD_NO);
26 if (!response)
27 return MHD_NO;
28
29 headervalue = malloc (strlen (strbase) + strlen (realm) + 1);
30 if (!headervalue)
31 return MHD_NO;
32
33 strcpy (headervalue, strbase);
34 strcat (headervalue, realm);
35
36 ret = MHD_add_response_header (response, "WWW-Authenticate", headervalue);
37 free (headervalue);
38 if (!ret)
39 {
40 MHD_destroy_response (response);
41 return MHD_NO;
42 }
43
44 ret = MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
45
46 MHD_destroy_response (response);
47
48 return ret;
49}
50
51static int
52is_authenticated (struct MHD_Connection *connection,
53 const char *username, const char *password)
54{
55 const char *headervalue;
56 char *expected_b64, *expected;
57 const char *strbase = "Basic ";
58 int authenticated;
59
60 headervalue =
61 MHD_lookup_connection_value (connection, MHD_HEADER_KIND,
62 "Authorization");
63 if (NULL == headervalue)
64 return 0;
65 if (0 != strncmp (headervalue, strbase, strlen (strbase)))
66 return 0;
67
68 expected = malloc (strlen (username) + 1 + strlen (password) + 1);
69 if (NULL == expected)
70 return 0;
71
72 strcpy (expected, username);
73 strcat (expected, ":");
74 strcat (expected, password);
75
76 expected_b64 = string_to_base64 (expected);
77 free (expected);
78 if (NULL == expected_b64)
79 return 0;
80
81 authenticated =
82 (strcmp (headervalue + strlen (strbase), expected_b64) == 0);
83
84 free (expected_b64);
85 return authenticated;
86}
87
88
89static int
90secret_page (struct MHD_Connection *connection)
91{
92 int ret;
93 struct MHD_Response *response;
94 const char *page = "<html><body>A secret.</body></html>";
95
96 response =
97 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
98 MHD_NO);
99 if (!response)
100 return MHD_NO;
101
102 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
103 MHD_destroy_response (response);
104
105 return ret;
106}
107
108 12
109static int 13static int
110answer_to_connection (void *cls, struct MHD_Connection *connection, 14answer_to_connection (void *cls, struct MHD_Connection *connection,
@@ -112,6 +16,12 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
112 const char *version, const char *upload_data, 16 const char *version, const char *upload_data,
113 size_t *upload_data_size, void **con_cls) 17 size_t *upload_data_size, void **con_cls)
114{ 18{
19 char *user;
20 char *pass;
21 int fail;
22 int ret;
23 struct MHD_Response *response;
24
115 if (0 != strcmp (method, "GET")) 25 if (0 != strcmp (method, "GET"))
116 return MHD_NO; 26 return MHD_NO;
117 if (NULL == *con_cls) 27 if (NULL == *con_cls)
@@ -119,11 +29,33 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
119 *con_cls = connection; 29 *con_cls = connection;
120 return MHD_YES; 30 return MHD_YES;
121 } 31 }
122 32 pass = NULL;
123 if (!is_authenticated (connection, USER, PASSWORD)) 33 user = MHD_basic_auth_get_username_password (connection, &pass);
124 return ask_for_authentication (connection, REALM); 34 fail = ( (user == NULL) ||
125 35 (0 != strcmp (user, "root")) ||
126 return secret_page (connection); 36 (0 != strcmp (pass, "pa$$w0rd") ) );
37 if (user != NULL) free (user);
38 if (pass != NULL) free (pass);
39 if (fail)
40 {
41 const char *page = "<html><body>Go away.</body></html>";
42 response =
43 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
44 MHD_NO);
45 ret = MHD_queue_basic_auth_fail_response (connection,
46 "my realm",
47 response);
48 }
49 else
50 {
51 const char *page = "<html><body>A secret.</body></html>";
52 response =
53 MHD_create_response_from_data (strlen (page), (void *) page, MHD_NO,
54 MHD_NO);
55 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
56 }
57 MHD_destroy_response (response);
58 return ret;
127} 59}
128 60
129 61
@@ -142,42 +74,3 @@ main ()
142 MHD_stop_daemon (daemon); 74 MHD_stop_daemon (daemon);
143 return 0; 75 return 0;
144} 76}
145
146
147char *
148string_to_base64 (const char *message)
149{
150 const char *lookup =
151 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
152 unsigned long l;
153 int i;
154 char *tmp;
155 size_t length = strlen (message);
156
157 tmp = malloc (length * 2);
158 if (NULL == tmp)
159 return tmp;
160
161 tmp[0] = 0;
162
163 for (i = 0; i < length; i += 3)
164 {
165 l = (((unsigned long) message[i]) << 16)
166 | (((i + 1) < length) ? (((unsigned long) message[i + 1]) << 8) : 0)
167 | (((i + 2) < length) ? ((unsigned long) message[i + 2]) : 0);
168
169
170 strncat (tmp, &lookup[(l >> 18) & 0x3F], 1);
171 strncat (tmp, &lookup[(l >> 12) & 0x3F], 1);
172
173 if (i + 1 < length)
174 strncat (tmp, &lookup[(l >> 6) & 0x3F], 1);
175 if (i + 2 < length)
176 strncat (tmp, &lookup[l & 0x3F], 1);
177 }
178
179 if (length % 3)
180 strncat (tmp, "===", 3 - length % 3);
181
182 return tmp;
183}
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"
106/** 106/**
107 * Current version of the library. 107 * Current version of the library.
108 */ 108 */
109#define MHD_VERSION 0x00090301 109#define MHD_VERSION 0x00090400
110 110
111/** 111/**
112 * MHD-internal return code for "YES". 112 * MHD-internal return code for "YES".