View Issue Details

IDProjectCategoryView StatusLast Update
0002059libmicrohttpddigest authentication (HTTP)public2012-01-23 14:21
Reportertclaveirole Assigned ToChristian Grothoff  
PrioritynormalSeverityminorReproducibilityalways
Status closedResolutionfixed 
Product Version0.9.17 
Target Version0.9.18Fixed in Version0.9.18 
Summary0002059: digest authentication fails with GET arguments
DescriptionHello,

I just found a bug in libmicrohttpd when using digest authentication
and URIs that include GET arguments. Here is a test case, an
explanation, and a fix.
Steps To ReproduceThe attached test.c file shows the bug. Its code is almost a straight
copy from libmicrohttpd's documentation (for the ahc_echo() function)
and tutorial (for the main() function). The only differences are
I added a fprintf() at the beginning of ahc_echo(), a few includes,
and changed the layout a bit.

When compiled, this test works properly for any URI without GET
arguments (using digest authentication). When one sends a request
with GET arguments, however, the authentication fails forever
(MHD_digest_auth_check() returns MHD_INVALID_NONCE). At some point
the server keep responding 401 errors that include stale=true in the
WWW-Authenticate header and therefore the client tries to
re-authenticate again. This ends up in an "infinite" loop of requests
and 401 responses until the client timeouts.
Additional Information* Explanation

MHD_queue_auth_fail_response() uses connection->url to compute the
server nonce (calling calculate_nonce(), line 608 of digestauth.c).
This string does not include GET arguments.

Now, MHD_digest_auth_check() checks the client-returned server nonce
was generated by libmicrohttpd (digestauth.c, line 523 to 541).
However, it does that by using the URI from the WWW-Authenticate
header to compute the "reference" server nonce to compare to, and this
URI includes GET arguments. Therefore, this test can never be true
and MHD_digest_auth_check() ought to fail on URIs that include GET
arguments.

* Fix

Attached is a patch to fix this bug. The simple solution is to
compute the "reference" server nonce in MHD_digest_auth_check() using
connection->url instead of the WWW-Authenticate given URI. After
a quick examination I do not think this breaks RFC 2617 or this is
a security issue. However, I am no expert.

Note that another fix could be to compute the server nonce in
MHD_queue_auth_fail_response() using an URI that includes GET
arguments. I do not know which solution is best. Anyway, I do not
know libmicrohttpd's internals enough to implement this second fix.
TagsNo tags attached.
Attached Files
test+patch.tar (10,240 bytes)

Activities

Christian Grothoff

2012-01-05 22:02

manager   ~0005245

I think this is mostly fine, except that it breaks checking that the URI we got on the server side matches at all the URI that the client believes he is getting. We can improve this a bit like this (SVN :

$ svn diff
Index: digestauth.c
===================================================================
--- digestauth.c (revision 19007)
+++ digestauth.c (working copy)
@@ -518,13 +518,16 @@
      * exceeds `nonce_timeout' then the nonce is
      * invalid.
      */
- if (t > nonce_time + nonce_timeout)
+ if ( (t > nonce_time + nonce_timeout) ||
+ (0 != strncmp (uri,
+ connection->url,
+ strlen (connection->url))) )
       return MHD_INVALID_NONCE;
     calculate_nonce (nonce_time,
                     connection->method,
                     connection->daemon->digest_auth_random,
                     connection->daemon->digest_auth_rand_size,
- uri,
+ connection->url,
                     realm,
                     noncehashexp);
     /*

Still, now some MiM could exchange the URI's query arguments for others. So the user authenticates for

 http://foo.bar/a?q=test

and a MiM could substitute

 http://foo.bar/a?q=evil

and the server would still accept the response. So an attacker would only be prevented by digest authentication from visiting

 http://foo.bar/b

with my variation (whereas yours would allow an attacker to access anything on http://foo.bar/ after stealing digest credentials -- at least until the nonce changes).

I'm not sure either is fully acceptable; however the second case could be documented and is at least cheap to implement. Reconstructing the full original URI at this time would be eh, much more difficult.

Christian Grothoff

2012-01-19 17:43

manager   ~0005295

SVN 19262 contains a patch that keeps the full check that the original URI and the URI provided in the authentication header are the same. It does a *lot* more work to do this, but avoids
1) having to keep around a copy of the original URI (as digest authentication is uncommon, the extra memory requirement would be bad)
2) allowing the same authentication values to be used for a URI with different arguments

This is done by parsing the URI from the authentication header and check that each of the key-value pairs matches those we have in the connection structure (and checking that they also have the same total number).

Issue History

Date Modified Username Field Change
2012-01-05 15:46 tclaveirole New Issue
2012-01-05 15:46 tclaveirole File Added: test+patch.tar
2012-01-05 22:02 Christian Grothoff Note Added: 0005245
2012-01-05 22:02 Christian Grothoff Assigned To => Christian Grothoff
2012-01-05 22:02 Christian Grothoff Status new => feedback
2012-01-05 22:03 Christian Grothoff Target Version => 0.9.18
2012-01-19 17:43 Christian Grothoff Note Added: 0005295
2012-01-19 17:43 Christian Grothoff Status feedback => resolved
2012-01-19 17:43 Christian Grothoff Fixed in Version => 0.9.18
2012-01-19 17:43 Christian Grothoff Resolution open => fixed
2012-01-23 14:21 Christian Grothoff Status resolved => closed
2013-05-06 12:52 Christian Grothoff Category digest authentication => digest authentication (HTTP)