aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2021-02-02 18:40:10 +0100
committerChristian Grothoff <christian@grothoff.org>2021-02-02 18:40:10 +0100
commite06693862812597b14b4eccd2c06f71dff6ca846 (patch)
treea90ce7bb4f4e4eb703cc5ea589205f7fcb0eafc9
parent0394bc7213794b463e7edc110cd3ce2ab3ee261b (diff)
downloadlibmicrohttpd-e06693862812597b14b4eccd2c06f71dff6ca846.tar.gz
libmicrohttpd-e06693862812597b14b4eccd2c06f71dff6ca846.zip
expand tutorial on connection phases, thanks to Igor for pointing out that the text was incomplete
-rw-r--r--doc/chapters/basicauthentication.inc132
-rw-r--r--doc/libmicrohttpd-tutorial.texi2
2 files changed, 83 insertions, 51 deletions
diff --git a/doc/chapters/basicauthentication.inc b/doc/chapters/basicauthentication.inc
index ec606f3e..ca48403a 100644
--- a/doc/chapters/basicauthentication.inc
+++ b/doc/chapters/basicauthentication.inc
@@ -1,7 +1,7 @@
1With the small exception of IP address based access control, 1With the small exception of IP address based access control,
2requests from all connecting clients where served equally until now. 2requests from all connecting clients where served equally until now.
3This chapter discusses a first method of client's authentication and 3This chapter discusses a first method of client's authentication and
4its limits. 4its limits.
5 5
6A very simple approach feasible with the means already discussed would 6A very simple approach feasible with the means already discussed would
7be to expect the password in the @emph{URI} string before granting access to 7be to expect the password in the @emph{URI} string before granting access to
@@ -12,68 +12,102 @@ GET /picture.png?mypassword
12@end verbatim 12@end verbatim
13@noindent 13@noindent
14 14
15In the rare situation where the client is customized enough and the connection occurs 15In the rare situation where the client is customized enough and the connection
16through secured lines (e.g., a embedded device directly attached to another via wire) 16occurs through secured lines (e.g., a embedded device directly attached to
17and where the ability to embed a password in the URI or to pass on a URI with a 17another via wire) and where the ability to embed a password in the URI or to
18password are desired, this can be a reasonable choice. 18pass on a URI with a password are desired, this can be a reasonable choice.
19 19
20But when it is assumed that the user connecting does so with an ordinary Internet browser, 20But when it is assumed that the user connecting does so with an ordinary
21this implementation brings some problems about. For example, the URI including the password 21Internet browser, this implementation brings some problems about. For example,
22stays in the address field or at least in the history of the browser for anybody near enough to see. 22the URI including the password stays in the address field or at least in the
23It will also be inconvenient to add the password manually to any new URI when the browser does 23history of the browser for anybody near enough to see. It will also be
24inconvenient to add the password manually to any new URI when the browser does
24not know how to compose this automatically. 25not know how to compose this automatically.
25 26
26At least the convenience issue can be addressed by employing the simplest built-in password 27At least the convenience issue can be addressed by employing the simplest
27facilities of HTTP compliant browsers, hence we want to start there. It will however turn out 28built-in password facilities of HTTP compliant browsers, hence we want to
28to have still severe weaknesses in terms of security which need consideration. 29start there. It will, however, turn out to have still severe weaknesses in
30terms of security which need consideration.
29 31
30Before we will start implementing @emph{Basic Authentication} as described in @emph{RFC 2617}, 32Before we will start implementing @emph{Basic Authentication} as described in
31we should finally abandon the bad practice of responding every request the first time our callback 33@emph{RFC 2617}, we will also abandon the simplistic and generally
32is called for a given connection. This is becoming more important now because the client and 34problematic practice of responding every request the first time our callback
33the server will have to talk in a more bi-directional way than before to 35is called for a given connection. Queuing a response upon the first request
36is akin to generating an error response (even if it is a "200 OK" reply!).
37The reason is that MHD usually calls the callback in three phases:
34 38
35But how can we tell whether the callback has been called before for the particular connection? 39@enumerate
36Initially, the pointer this parameter references is set by @emph{MHD} in the callback. But it will 40@item
37also be "remembered" on the next call (for the same connection). 41First, to initially tell the application about the connection and inquire whether
38Thus, we will generate no response until the parameter is non-null---implying the callback was 42it is OK to proceed. This call typically happens before the client could upload
39called before at least once. We do not need to share information between different calls of the callback, 43the request body, and can be used to tell the client to not proceed with the
40so we can set the parameter to any address that is assured to be not null. The pointer to the 44upload (if the client requested "Expect: 100 Continue"). Applications may queue
41@code{connection} structure will be pointing to a legal address, so we take this. 45a reply at this point, but it will force the connection to be closed and thus
46prevent keep-alive / pipelining, which is generally a bad idea. Applications
47wanting to proceed with the request throughout the other phases should just return
48"MHD_YES" and not queue any response. Note that when an application suspends
49a connection in this callback, the phase does not advance and the application
50will be called again in this first phase.
51@item
52Next, to tell the application about upload data provided by the client.
53In this phase, the application may not queue replies, and trying to do so
54will result in MHD returning an error code from @code{MHD_queue_response}.
55If there is no upload data, this phase is skipped.
56@item
57Finally, to obtain a regular response from the application. This can be
58almost any type of response, including ones indicating failures. The
59one exception is a "100 Continue" response, which applications must never
60generate: MHD generates that response automatically when necessary in the
61first phase. If the application does not queue a response, MHD may call
62the callback repeatedly (depending a bit on the threading model, the
63application should suspend the connection).
64@end enumerate
65
66But how can we tell whether the callback has been called before for the
67particular connection? Initially, the pointer this parameter references is
68set by @emph{MHD} in the callback. But it will also be "remembered" on the
69next call (for the same connection). Thus, we can use the @code{con_cls}
70location to keep track of the connection state. For now, we will simply
71generate no response until the parameter is non-null---implying the callback
72was called before at least once. We do not need to share information between
73different calls of the callback, so we can set the parameter to any address
74that is assured to be not null. The pointer to the @code{connection} structure
75will be pointing to a legal address, so we take this.
42 76
43The first time @code{answer_to_connection} is called, we will not even look at the headers. 77The first time @code{answer_to_connection} is called, we will not even look at the headers.
44 78
45@verbatim 79@verbatim
46static int 80static int
47answer_to_connection (void *cls, struct MHD_Connection *connection, 81answer_to_connection (void *cls, struct MHD_Connection *connection,
48 const char *url, const char *method, const char *version, 82 const char *url, const char *method, const char *version,
49 const char *upload_data, size_t *upload_data_size, 83 const char *upload_data, size_t *upload_data_size,
50 void **con_cls) 84 void **con_cls)
51{ 85{
52 if (0 != strcmp(method, "GET")) return MHD_NO; 86 if (0 != strcmp(method, "GET")) return MHD_NO;
53 if (NULL == *con_cls) {*con_cls = connection; return MHD_YES;} 87 if (NULL == *con_cls) {*con_cls = connection; return MHD_YES;}
54 88
55 ... 89 ...
56 /* else respond accordingly */ 90 /* else respond accordingly */
57 ... 91 ...
58} 92}
59@end verbatim 93@end verbatim
60@noindent 94@noindent
61 95
62Note how we lop off the connection on the first condition (no "GET" request), but return asking for more on 96Note how we lop off the connection on the first condition (no "GET" request),
63the other one with @code{MHD_YES}. 97but return asking for more on the other one with @code{MHD_YES}. With this
64With this minor change, we can proceed to implement the actual authentication process. 98minor change, we can proceed to implement the actual authentication process.
65 99
66@heading Request for authentication 100@heading Request for authentication
67 101
68Let us assume we had only files not intended to be handed out without the correct username/password, 102Let us assume we had only files not intended to be handed out without the
69so every "GET" request will be challenged. 103correct username/password, so every "GET" request will be challenged.
70@emph{RFC 2617} describes how the server shall ask for authentication by adding a 104@emph{RFC 2617} describes how the server shall ask for authentication by
71@emph{WWW-Authenticate} response header with the name of the @emph{realm} protected. 105adding a @emph{WWW-Authenticate} response header with the name of the
72MHD can generate and queue such a failure response for you using 106@emph{realm} protected. MHD can generate and queue such a failure response
73the @code{MHD_queue_basic_auth_fail_response} API. The only thing you need to do 107for you using the @code{MHD_queue_basic_auth_fail_response} API. The only
74is construct a response with the error page to be shown to the user 108thing you need to do is construct a response with the error page to be shown
75if he aborts basic authentication. But first, you should check if the 109to the user if he aborts basic authentication. But first, you should check if
76proper credentials were already supplied using the 110the proper credentials were already supplied using the
77@code{MHD_basic_auth_get_username_password} call. 111@code{MHD_basic_auth_get_username_password} call.
78 112
79Your code would then look like this: 113Your code would then look like this:
@@ -101,14 +135,14 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
101 user = MHD_basic_auth_get_username_password (connection, &pass); 135 user = MHD_basic_auth_get_username_password (connection, &pass);
102 fail = ( (user == NULL) || 136 fail = ( (user == NULL) ||
103 (0 != strcmp (user, "root")) || 137 (0 != strcmp (user, "root")) ||
104 (0 != strcmp (pass, "pa$$w0rd") ) ); 138 (0 != strcmp (pass, "pa$$w0rd") ) );
105 if (user != NULL) free (user); 139 if (user != NULL) free (user);
106 if (pass != NULL) free (pass); 140 if (pass != NULL) free (pass);
107 if (fail) 141 if (fail)
108 { 142 {
109 const char *page = "<html><body>Go away.</body></html>"; 143 const char *page = "<html><body>Go away.</body></html>";
110 response = 144 response =
111 MHD_create_response_from_buffer (strlen (page), (void *) page, 145 MHD_create_response_from_buffer (strlen (page), (void *) page,
112 MHD_RESPMEM_PERSISTENT); 146 MHD_RESPMEM_PERSISTENT);
113 ret = MHD_queue_basic_auth_fail_response (connection, 147 ret = MHD_queue_basic_auth_fail_response (connection,
114 "my realm", 148 "my realm",
@@ -118,7 +152,7 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
118 { 152 {
119 const char *page = "<html><body>A secret.</body></html>"; 153 const char *page = "<html><body>A secret.</body></html>";
120 response = 154 response =
121 MHD_create_response_from_buffer (strlen (page), (void *) page, 155 MHD_create_response_from_buffer (strlen (page), (void *) page,
122 MHD_RESPMEM_PERSISTENT); 156 MHD_RESPMEM_PERSISTENT);
123 ret = MHD_queue_response (connection, MHD_HTTP_OK, response); 157 ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
124 } 158 }
@@ -130,9 +164,9 @@ answer_to_connection (void *cls, struct MHD_Connection *connection,
130See the @code{examples} directory for the complete example file. 164See the @code{examples} directory for the complete example file.
131 165
132@heading Remarks 166@heading Remarks
133For a proper server, the conditional statements leading to a return of @code{MHD_NO} should yield a 167For a proper server, the conditional statements leading to a return of @code{MHD_NO} should yield a
134response with a more precise status code instead of silently closing the connection. For example, 168response with a more precise status code instead of silently closing the connection. For example,
135failures of memory allocation are best reported as @emph{internal server error} and unexpected 169failures of memory allocation are best reported as @emph{internal server error} and unexpected
136authentication methods as @emph{400 bad request}. 170authentication methods as @emph{400 bad request}.
137 171
138@heading Exercises 172@heading Exercises
@@ -142,7 +176,7 @@ Make the server respond to wrong credentials (but otherwise well-formed requests
142@emph{401 unauthorized} status code. If the client still does not authenticate correctly within the 176@emph{401 unauthorized} status code. If the client still does not authenticate correctly within the
143same connection, close it and store the client's IP address for a certain time. (It is OK to check for 177same connection, close it and store the client's IP address for a certain time. (It is OK to check for
144expiration not until the main thread wakes up again on the next connection.) If the client fails 178expiration not until the main thread wakes up again on the next connection.) If the client fails
145authenticating three times during this period, add it to another list for which the 179authenticating three times during this period, add it to another list for which the
146@code{AcceptPolicyCallback} function denies connection (temporally). 180@code{AcceptPolicyCallback} function denies connection (temporally).
147 181
148@item 182@item
@@ -156,5 +190,3 @@ Copy and paste the encoded string you see in @code{netcat}'s output to some of t
156and see how both the user's name and password could be completely restored. 190and see how both the user's name and password could be completely restored.
157 191
158@end itemize 192@end itemize
159
160
diff --git a/doc/libmicrohttpd-tutorial.texi b/doc/libmicrohttpd-tutorial.texi
index 8fd7b566..de040fe8 100644
--- a/doc/libmicrohttpd-tutorial.texi
+++ b/doc/libmicrohttpd-tutorial.texi
@@ -24,7 +24,7 @@ updated @value{UPDATED}.
24 24
25Copyright (c) 2008 Sebastian Gerhardt. 25Copyright (c) 2008 Sebastian Gerhardt.
26 26
27Copyright (c) 2010, 2011, 2012, 2013, 2016 Christian Grothoff. 27Copyright (c) 2010, 2011, 2012, 2013, 2016, 2021 Christian Grothoff.
28@quotation 28@quotation
29Permission is granted to copy, distribute and/or modify this document 29Permission is granted to copy, distribute and/or modify this document
30under the terms of the GNU Free Documentation License, Version 1.3 30under the terms of the GNU Free Documentation License, Version 1.3