diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-02-02 18:40:10 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-02-02 18:40:10 +0100 |
commit | e06693862812597b14b4eccd2c06f71dff6ca846 (patch) | |
tree | a90ce7bb4f4e4eb703cc5ea589205f7fcb0eafc9 | |
parent | 0394bc7213794b463e7edc110cd3ce2ab3ee261b (diff) |
expand tutorial on connection phases, thanks to Igor for pointing out that the text was incomplete
-rw-r--r-- | doc/chapters/basicauthentication.inc | 132 | ||||
-rw-r--r-- | doc/libmicrohttpd-tutorial.texi | 2 |
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 @@ -With the small exception of IP address based access control, +With the small exception of IP address based access control, requests from all connecting clients where served equally until now. This chapter discusses a first method of client's authentication and -its limits. +its limits. A very simple approach feasible with the means already discussed would be to expect the password in the @emph{URI} string before granting access to @@ -12,68 +12,102 @@ GET /picture.png?mypassword @end verbatim @noindent -In the rare situation where the client is customized enough and the connection occurs -through secured lines (e.g., a embedded device directly attached to another via wire) -and where the ability to embed a password in the URI or to pass on a URI with a -password are desired, this can be a reasonable choice. +In the rare situation where the client is customized enough and the connection +occurs through secured lines (e.g., a embedded device directly attached to +another via wire) and where the ability to embed a password in the URI or to +pass on a URI with a password are desired, this can be a reasonable choice. -But when it is assumed that the user connecting does so with an ordinary Internet browser, -this implementation brings some problems about. For example, the URI including the password -stays in the address field or at least in the history of the browser for anybody near enough to see. -It will also be inconvenient to add the password manually to any new URI when the browser does +But when it is assumed that the user connecting does so with an ordinary +Internet browser, this implementation brings some problems about. For example, +the URI including the password stays in the address field or at least in the +history of the browser for anybody near enough to see. It will also be +inconvenient to add the password manually to any new URI when the browser does not know how to compose this automatically. -At least the convenience issue can be addressed by employing the simplest built-in password -facilities of HTTP compliant browsers, hence we want to start there. It will however turn out -to have still severe weaknesses in terms of security which need consideration. +At least the convenience issue can be addressed by employing the simplest +built-in password facilities of HTTP compliant browsers, hence we want to +start there. It will, however, turn out to have still severe weaknesses in +terms of security which need consideration. -Before we will start implementing @emph{Basic Authentication} as described in @emph{RFC 2617}, -we should finally abandon the bad practice of responding every request the first time our callback -is called for a given connection. This is becoming more important now because the client and -the server will have to talk in a more bi-directional way than before to +Before we will start implementing @emph{Basic Authentication} as described in +@emph{RFC 2617}, we will also abandon the simplistic and generally +problematic practice of responding every request the first time our callback +is called for a given connection. Queuing a response upon the first request +is akin to generating an error response (even if it is a "200 OK" reply!). +The reason is that MHD usually calls the callback in three phases: -But how can we tell whether the callback has been called before for the particular connection? -Initially, the pointer this parameter references is set by @emph{MHD} in the callback. But it will -also be "remembered" on the next call (for the same connection). -Thus, we will generate no response until the parameter is non-null---implying the callback was -called before at least once. We do not need to share information between different calls of the callback, -so we can set the parameter to any address that is assured to be not null. The pointer to the -@code{connection} structure will be pointing to a legal address, so we take this. +@enumerate +@item +First, to initially tell the application about the connection and inquire whether +it is OK to proceed. This call typically happens before the client could upload +the request body, and can be used to tell the client to not proceed with the +upload (if the client requested "Expect: 100 Continue"). Applications may queue +a reply at this point, but it will force the connection to be closed and thus +prevent keep-alive / pipelining, which is generally a bad idea. Applications +wanting to proceed with the request throughout the other phases should just return +"MHD_YES" and not queue any response. Note that when an application suspends +a connection in this callback, the phase does not advance and the application +will be called again in this first phase. +@item +Next, to tell the application about upload data provided by the client. +In this phase, the application may not queue replies, and trying to do so +will result in MHD returning an error code from @code{MHD_queue_response}. +If there is no upload data, this phase is skipped. +@item +Finally, to obtain a regular response from the application. This can be +almost any type of response, including ones indicating failures. The +one exception is a "100 Continue" response, which applications must never +generate: MHD generates that response automatically when necessary in the +first phase. If the application does not queue a response, MHD may call +the callback repeatedly (depending a bit on the threading model, the +application should suspend the connection). +@end enumerate + +But how can we tell whether the callback has been called before for the +particular connection? Initially, the pointer this parameter references is +set by @emph{MHD} in the callback. But it will also be "remembered" on the +next call (for the same connection). Thus, we can use the @code{con_cls} +location to keep track of the connection state. For now, we will simply +generate no response until the parameter is non-null---implying the callback +was called before at least once. We do not need to share information between +different calls of the callback, so we can set the parameter to any address +that is assured to be not null. The pointer to the @code{connection} structure +will be pointing to a legal address, so we take this. The first time @code{answer_to_connection} is called, we will not even look at the headers. @verbatim -static int +static int answer_to_connection (void *cls, struct MHD_Connection *connection, - const char *url, const char *method, const char *version, + const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **con_cls) { if (0 != strcmp(method, "GET")) return MHD_NO; if (NULL == *con_cls) {*con_cls = connection; return MHD_YES;} - ... + ... /* else respond accordingly */ ... } @end verbatim @noindent -Note how we lop off the connection on the first condition (no "GET" request), but return asking for more on -the other one with @code{MHD_YES}. -With this minor change, we can proceed to implement the actual authentication process. - -@heading Request for authentication - -Let us assume we had only files not intended to be handed out without the correct username/password, -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 +Note how we lop off the connection on the first condition (no "GET" request), +but return asking for more on the other one with @code{MHD_YES}. With this +minor change, we can proceed to implement the actual authentication process. + +@heading Request for authentication + +Let us assume we had only files not intended to be handed out without the +correct username/password, 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. Your code would then look like this: @@ -101,14 +135,14 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, user = MHD_basic_auth_get_username_password (connection, &pass); fail = ( (user == NULL) || (0 != strcmp (user, "root")) || - (0 != strcmp (pass, "pa$$w0rd") ) ); + (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_buffer (strlen (page), (void *) page, + MHD_create_response_from_buffer (strlen (page), (void *) page, MHD_RESPMEM_PERSISTENT); ret = MHD_queue_basic_auth_fail_response (connection, "my realm", @@ -118,7 +152,7 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, { const char *page = "<html><body>A secret.</body></html>"; response = - MHD_create_response_from_buffer (strlen (page), (void *) page, + MHD_create_response_from_buffer (strlen (page), (void *) page, MHD_RESPMEM_PERSISTENT); ret = MHD_queue_response (connection, MHD_HTTP_OK, response); } @@ -130,9 +164,9 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, See the @code{examples} directory for the complete example file. @heading Remarks -For a proper server, the conditional statements leading to a return of @code{MHD_NO} should yield a +For a proper server, the conditional statements leading to a return of @code{MHD_NO} should yield a response with a more precise status code instead of silently closing the connection. For example, -failures of memory allocation are best reported as @emph{internal server error} and unexpected +failures of memory allocation are best reported as @emph{internal server error} and unexpected authentication methods as @emph{400 bad request}. @heading Exercises @@ -142,7 +176,7 @@ Make the server respond to wrong credentials (but otherwise well-formed requests @emph{401 unauthorized} status code. If the client still does not authenticate correctly within the same connection, close it and store the client's IP address for a certain time. (It is OK to check for expiration not until the main thread wakes up again on the next connection.) If the client fails -authenticating three times during this period, add it to another list for which the +authenticating three times during this period, add it to another list for which the @code{AcceptPolicyCallback} function denies connection (temporally). @item @@ -156,5 +190,3 @@ Copy and paste the encoded string you see in @code{netcat}'s output to some of t and see how both the user's name and password could be completely restored. @end itemize - - 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}. Copyright (c) 2008 Sebastian Gerhardt. -Copyright (c) 2010, 2011, 2012, 2013, 2016 Christian Grothoff. +Copyright (c) 2010, 2011, 2012, 2013, 2016, 2021 Christian Grothoff. @quotation Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 |