aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/digestauth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r--src/microhttpd/digestauth.c779
1 files changed, 514 insertions, 265 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 80cba836..424c3761 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -21,11 +21,13 @@
21 * @brief Implements HTTP digest authentication 21 * @brief Implements HTTP digest authentication
22 * @author Amr Ali 22 * @author Amr Ali
23 * @author Matthieu Speder 23 * @author Matthieu Speder
24 * @author Christian Grothoff (RFC 7616 support)
24 */ 25 */
25#include "platform.h" 26#include "platform.h"
26#include "mhd_limits.h" 27#include "mhd_limits.h"
27#include "internal.h" 28#include "internal.h"
28#include "md5.h" 29#include "md5.h"
30#include "sha256.h"
29#include "mhd_mono_clock.h" 31#include "mhd_mono_clock.h"
30#include "mhd_str.h" 32#include "mhd_str.h"
31#include "mhd_compat.h" 33#include "mhd_compat.h"
@@ -37,13 +39,18 @@
37#include <windows.h> 39#include <windows.h>
38#endif /* MHD_W32_MUTEX_ */ 40#endif /* MHD_W32_MUTEX_ */
39 41
40#define HASH_MD5_HEX_LEN (2 * MHD_MD5_DIGEST_SIZE) 42/**
41/* 32 bit value is 4 bytes */ 43 * 32 bit value is 4 bytes
44 */
42#define TIMESTAMP_BIN_SIZE 4 45#define TIMESTAMP_BIN_SIZE 4
43#define TIMESTAMP_HEX_LEN (2 * TIMESTAMP_BIN_SIZE)
44 46
45/* Standard server nonce length, not including terminating null */ 47/**
46#define NONCE_STD_LEN (HASH_MD5_HEX_LEN + TIMESTAMP_HEX_LEN) 48 * Standard server nonce length, not including terminating null,
49 *
50 * @param digest_size digest size
51 */
52#define NONCE_STD_LEN(digest_size) \
53 ((digest_size) * 2 + TIMESTAMP_BIN_SIZE * 2)
47 54
48/** 55/**
49 * Beginning string for any valid Digest authentication header. 56 * Beginning string for any valid Digest authentication header.
@@ -63,7 +70,66 @@
63/** 70/**
64 * Maximum length of the response in digest authentication. 71 * Maximum length of the response in digest authentication.
65 */ 72 */
66#define MAX_AUTH_RESPONSE_LENGTH 128 73#define MAX_AUTH_RESPONSE_LENGTH 256
74
75
76/**
77 * Context passed to functions that need to calculate
78 * a digest but are orthogonal to the specific
79 * algorithm.
80 */
81struct DigestAlgorithm
82{
83 /**
84 * Size of the final digest returned by @e digest.
85 */
86 unsigned int digest_size;
87
88 /**
89 * A context for the digest algorithm, already initialized to be
90 * useful for @e init, @e update and @e digest.
91 */
92 void *ctx;
93
94 /**
95 * Name of the algorithm, "md5" or "sha-256"
96 */
97 const char *alg;
98
99 /**
100 * Buffer of @e digest_size * 2 + 1 bytes.
101 */
102 char *sessionkey;
103
104 /**
105 * Call to initialize @e ctx.
106 */
107 void
108 (*init)(void *ctx);
109
110 /**
111 * Feed more data into the digest function.
112 *
113 * @param ctx context to feed
114 * @param length number of bytes in @a data
115 * @param data data to add
116 */
117 void
118 (*update)(void *ctx,
119 const uint8_t *data,
120 size_t length);
121
122 /**
123 * Compute final @a digest.
124 *
125 * @param ctx context to use
126 * @param digest[out] where to write the result,
127 * must be @e digest_length bytes long
128 */
129 void
130 (*digest)(void *ctx,
131 uint8_t *digest);
132};
67 133
68 134
69/** 135/**
@@ -97,54 +163,57 @@ cvthex (const unsigned char *bin,
97 * and store the * result in 'sessionkey'. 163 * and store the * result in 'sessionkey'.
98 * 164 *
99 * @param alg The hash algorithm used, can be "md5" or "md5-sess" 165 * @param alg The hash algorithm used, can be "md5" or "md5-sess"
166 * or "sha-256" or "sha-256-sess"
167 * Note that the rest of the code does not support the the "-sess" variants!
168 * @param da[in,out] digest implementation, must match @a alg; the
169 * da->sessionkey will be initialized to the digest in HEX
100 * @param digest An `unsigned char *' pointer to the binary MD5 sum 170 * @param digest An `unsigned char *' pointer to the binary MD5 sum
101 * for the precalculated hash value "username:realm:password" 171 * for the precalculated hash value "username:realm:password"
102 * of #MHD_MD5_DIGEST_SIZE bytes 172 * of #MHD_MD5_DIGEST_SIZE or #MHD_SHA256_DIGEST_SIZE bytes
103 * @param nonce A `char *' pointer to the nonce value 173 * @param nonce A `char *' pointer to the nonce value
104 * @param cnonce A `char *' pointer to the cnonce value 174 * @param cnonce A `char *' pointer to the cnonce value
105 * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
106 */ 175 */
107static void 176static void
108digest_calc_ha1_from_digest (const char *alg, 177digest_calc_ha1_from_digest (const char *alg,
109 const uint8_t digest[MHD_MD5_DIGEST_SIZE], 178 struct DigestAlgorithm *da,
179 const uint8_t *digest,
110 const char *nonce, 180 const char *nonce,
111 const char *cnonce, 181 const char *cnonce)
112 char sessionkey[HASH_MD5_HEX_LEN + 1])
113{ 182{
114 struct MD5Context md5; 183 if ( (MHD_str_equal_caseless_(alg,
115 184 "md5-sess")) ||
116 if (MHD_str_equal_caseless_(alg, 185 (MHD_str_equal_caseless_(alg,
117 "md5-sess")) 186 "sha-256-sess")) )
118 { 187 {
119 unsigned char ha1[MHD_MD5_DIGEST_SIZE]; 188 uint8_t dig[da->digest_size];
120 189
121 MD5Init (&md5); 190 da->init (da->ctx);
122 MD5Update (&md5, 191 da->update (da->ctx,
123 digest, 192 digest,
124 MHD_MD5_DIGEST_SIZE); 193 MHD_MD5_DIGEST_SIZE);
125 MD5Update (&md5, 194 da->update (da->ctx,
195 (const unsigned char *) ":",
196 1);
197 da->update (da->ctx,
198 (const unsigned char *) nonce,
199 strlen (nonce));
200 da->update (da->ctx,
126 (const unsigned char *) ":", 201 (const unsigned char *) ":",
127 1); 202 1);
128 MD5Update (&md5, 203 da->update (da->ctx,
129 (const unsigned char *) nonce, 204 (const unsigned char *) cnonce,
130 strlen (nonce)); 205 strlen (cnonce));
131 MD5Update (&md5, 206 da->digest (da->ctx,
132 (const unsigned char *) ":", 207 dig);
133 1); 208 cvthex (dig,
134 MD5Update (&md5, 209 sizeof (dig),
135 (const unsigned char *) cnonce, 210 da->sessionkey);
136 strlen (cnonce));
137 MD5Final (ha1,
138 &md5);
139 cvthex (ha1,
140 sizeof (ha1),
141 sessionkey);
142 } 211 }
143 else 212 else
144 { 213 {
145 cvthex (digest, 214 cvthex (digest,
146 MHD_MD5_DIGEST_SIZE, 215 da->digest_size,
147 sessionkey); 216 da->sessionkey);
148 } 217 }
149} 218}
150 219
@@ -154,12 +223,14 @@ digest_calc_ha1_from_digest (const char *alg,
154 * and store the result in 'sessionkey'. 223 * and store the result in 'sessionkey'.
155 * 224 *
156 * @param alg The hash algorithm used, can be "md5" or "md5-sess" 225 * @param alg The hash algorithm used, can be "md5" or "md5-sess"
226 * or "sha-256" or "sha-256-sess"
157 * @param username A `char *' pointer to the username value 227 * @param username A `char *' pointer to the username value
158 * @param realm A `char *' pointer to the realm value 228 * @param realm A `char *' pointer to the realm value
159 * @param password A `char *' pointer to the password value 229 * @param password A `char *' pointer to the password value
160 * @param nonce A `char *' pointer to the nonce value 230 * @param nonce A `char *' pointer to the nonce value
161 * @param cnonce A `char *' pointer to the cnonce value 231 * @param cnonce A `char *' pointer to the cnonce value
162 * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes 232 * @param da[in,out] digest algorithm to use, and where to write
233 * the sessionkey to
163 */ 234 */
164static void 235static void
165digest_calc_ha1_from_user (const char *alg, 236digest_calc_ha1_from_user (const char *alg,
@@ -168,52 +239,54 @@ digest_calc_ha1_from_user (const char *alg,
168 const char *password, 239 const char *password,
169 const char *nonce, 240 const char *nonce,
170 const char *cnonce, 241 const char *cnonce,
171 char sessionkey[HASH_MD5_HEX_LEN + 1]) 242 struct DigestAlgorithm *da)
172{ 243{
173 struct MD5Context md5; 244 unsigned char ha1[da->digest_size];
174 unsigned char ha1[MHD_MD5_DIGEST_SIZE];
175 245
176 MD5Init (&md5); 246 da->init (da->ctx);
177 MD5Update (&md5, 247 da->update (da->ctx,
178 (const unsigned char *) username, 248 (const unsigned char *) username,
179 strlen (username)); 249 strlen (username));
180 MD5Update (&md5, 250 da->update (da->ctx,
181 (const unsigned char *) ":", 251 (const unsigned char *) ":",
182 1); 252 1);
183 MD5Update (&md5, 253 da->update (da->ctx,
184 (const unsigned char *) realm, 254 (const unsigned char *) realm,
185 strlen (realm)); 255 strlen (realm));
186 MD5Update (&md5, 256 da->update (da->ctx,
187 (const unsigned char *) ":", 257 (const unsigned char *) ":",
188 1); 258 1);
189 MD5Update (&md5, 259 da->update (da->ctx,
190 (const unsigned char *) password, 260 (const unsigned char *) password,
191 strlen (password)); 261 strlen (password));
192 MD5Final (ha1, 262 da->digest (da->ctx,
193 &md5); 263 ha1);
194 digest_calc_ha1_from_digest(alg, 264 digest_calc_ha1_from_digest (alg,
195 ha1, 265 da,
196 nonce, 266 ha1,
197 cnonce, 267 nonce,
198 sessionkey); 268 cnonce);
199} 269}
200 270
201 271
202/** 272/**
203 * Calculate request-digest/response-digest as per RFC2617 spec 273 * Calculate request-digest/response-digest as per RFC2617 / RFC7616
274 * spec.
204 * 275 *
205 * @param ha1 H(A1) 276 * @param ha1 H(A1), twice the @a da->digest_size + 1 bytes (0-terminated),
277 * MUST NOT be aliased with `da->sessionkey`!
206 * @param nonce nonce from server 278 * @param nonce nonce from server
207 * @param noncecount 8 hex digits 279 * @param noncecount 8 hex digits
208 * @param cnonce client nonce 280 * @param cnonce client nonce
209 * @param qop qop-value: "", "auth" or "auth-int" 281 * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.)
210 * @param method method from request 282 * @param method method from request
211 * @param uri requested URL 283 * @param uri requested URL
212 * @param hentity H(entity body) if qop="auth-int" 284 * @param hentity H(entity body) if qop="auth-int"
213 * @param response request-digest or response-digest 285 * @param da[in,out] digest algorithm to use, also
286 * we write da->sessionkey (set to response request-digest or response-digest)
214 */ 287 */
215static void 288static void
216digest_calc_response (const char ha1[HASH_MD5_HEX_LEN + 1], 289digest_calc_response (const char *ha1,
217 const char *nonce, 290 const char *nonce,
218 const char *noncecount, 291 const char *noncecount,
219 const char *cnonce, 292 const char *cnonce,
@@ -221,87 +294,85 @@ digest_calc_response (const char ha1[HASH_MD5_HEX_LEN + 1],
221 const char *method, 294 const char *method,
222 const char *uri, 295 const char *uri,
223 const char *hentity, 296 const char *hentity,
224 char response[HASH_MD5_HEX_LEN + 1]) 297 struct DigestAlgorithm *da)
225{ 298{
226 struct MD5Context md5; 299 unsigned char ha2[da->digest_size];
227 unsigned char ha2[MHD_MD5_DIGEST_SIZE]; 300 unsigned char resphash[da->digest_size];
228 unsigned char resphash[MHD_MD5_DIGEST_SIZE]; 301 (void)hentity; /* Unused. Silence compiler warning. */
229 char ha2hex[HASH_MD5_HEX_LEN + 1]; 302
230 (void)hentity; /* Unused. Silent compiler warning. */ 303 da->init (da->ctx);
231 304 da->update (da->ctx,
232 MD5Init (&md5); 305 (const unsigned char *) method,
233 MD5Update (&md5, 306 strlen (method));
234 (const unsigned char *) method, 307 da->update (da->ctx,
235 strlen (method)); 308 (const unsigned char *) ":",
236 MD5Update (&md5, 309 1);
237 (const unsigned char *) ":", 310 da->update (da->ctx,
238 1);
239 MD5Update (&md5,
240 (const unsigned char *) uri, 311 (const unsigned char *) uri,
241 strlen (uri)); 312 strlen (uri));
242#if 0 313#if 0
243 if (0 == strcasecmp(qop, 314 if (0 == strcasecmp (qop,
244 "auth-int")) 315 "auth-int"))
245 { 316 {
246 /* This is dead code since the rest of this module does 317 /* This is dead code since the rest of this module does
247 not support auth-int. */ 318 not support auth-int. */
248 MD5Update (&md5, 319 da->update (da->ctx,
249 ":", 320 ":",
250 1); 321 1);
251 if (NULL != hentity) 322 if (NULL != hentity)
252 MD5Update (&md5, 323 da->update (da->ctx,
253 hentity, 324 hentity,
254 strlen (hentity)); 325 strlen (hentity));
255 } 326 }
256#endif 327#endif
257 MD5Final (ha2, 328 da->digest (da->ctx,
258 &md5); 329 ha2);
259 cvthex (ha2, 330 cvthex (ha2,
260 MHD_MD5_DIGEST_SIZE, 331 da->digest_size,
261 ha2hex); 332 da->sessionkey);
262 MD5Init (&md5); 333 da->init (da->ctx);
263 /* calculate response */ 334 /* calculate response */
264 MD5Update (&md5, 335 da->update (da->ctx,
265 (const unsigned char *) ha1, 336 (const unsigned char *) ha1,
266 HASH_MD5_HEX_LEN); 337 da->digest_size * 2);
267 MD5Update (&md5, 338 da->update (da->ctx,
268 (const unsigned char *) ":", 339 (const unsigned char *) ":",
269 1); 340 1);
270 MD5Update (&md5, 341 da->update (da->ctx,
271 (const unsigned char *) nonce, 342 (const unsigned char *) nonce,
272 strlen (nonce)); 343 strlen (nonce));
273 MD5Update (&md5, 344 da->update (da->ctx,
274 (const unsigned char*) ":", 345 (const unsigned char*) ":",
275 1); 346 1);
276 if ('\0' != *qop) 347 if ('\0' != *qop)
277 { 348 {
278 MD5Update (&md5, 349 da->update (da->ctx,
279 (const unsigned char *) noncecount, 350 (const unsigned char *) noncecount,
280 strlen (noncecount)); 351 strlen (noncecount));
281 MD5Update (&md5, 352 da->update (da->ctx,
282 (const unsigned char *) ":", 353 (const unsigned char *) ":",
283 1); 354 1);
284 MD5Update (&md5, 355 da->update (da->ctx,
285 (const unsigned char *) cnonce, 356 (const unsigned char *) cnonce,
286 strlen (cnonce)); 357 strlen (cnonce));
287 MD5Update (&md5, 358 da->update (da->ctx,
288 (const unsigned char *) ":", 359 (const unsigned char *) ":",
289 1); 360 1);
290 MD5Update (&md5, 361 da->update (da->ctx,
291 (const unsigned char *) qop, 362 (const unsigned char *) qop,
292 strlen (qop)); 363 strlen (qop));
293 MD5Update (&md5, 364 da->update (da->ctx,
294 (const unsigned char *) ":", 365 (const unsigned char *) ":",
295 1); 366 1);
296 } 367 }
297 MD5Update (&md5, 368 da->update (da->ctx,
298 (const unsigned char *) ha2hex, 369 (const unsigned char *) da->sessionkey,
299 HASH_MD5_HEX_LEN); 370 da->digest_size * 2);
300 MD5Final (resphash, 371 da->digest (da->ctx,
301 &md5); 372 resphash);
302 cvthex (resphash, 373 cvthex (resphash,
303 sizeof(resphash), 374 sizeof(resphash),
304 response); 375 da->sessionkey);
305} 376}
306 377
307 378
@@ -552,7 +623,9 @@ MHD_digest_auth_get_username(struct MHD_Connection *connection)
552 * @param rnd_size The size of the random seed array @a rnd 623 * @param rnd_size The size of the random seed array @a rnd
553 * @param uri HTTP URI (in MHD, without the arguments ("?k=v") 624 * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
554 * @param realm A string of characters that describes the realm of auth. 625 * @param realm A string of characters that describes the realm of auth.
555 * @param nonce A pointer to a character array for the nonce to put in 626 * @param da digest algorithm to use
627 * @param nonce A pointer to a character array for the nonce to put in,
628 * must provide NONCE_STD_LEN(da->digest_size)+1 bytes
556 */ 629 */
557static void 630static void
558calculate_nonce (uint32_t nonce_time, 631calculate_nonce (uint32_t nonce_time,
@@ -561,48 +634,48 @@ calculate_nonce (uint32_t nonce_time,
561 size_t rnd_size, 634 size_t rnd_size,
562 const char *uri, 635 const char *uri,
563 const char *realm, 636 const char *realm,
564 char nonce[NONCE_STD_LEN + 1]) 637 struct DigestAlgorithm *da,
638 char *nonce)
565{ 639{
566 struct MD5Context md5;
567 unsigned char timestamp[TIMESTAMP_BIN_SIZE]; 640 unsigned char timestamp[TIMESTAMP_BIN_SIZE];
568 unsigned char tmpnonce[MHD_MD5_DIGEST_SIZE]; 641 unsigned char tmpnonce[da->digest_size];
569 char timestamphex[TIMESTAMP_HEX_LEN + 1]; 642 char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
570 643
571 MD5Init (&md5); 644 da->init (da->ctx);
572 timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18); 645 timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18);
573 timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10); 646 timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10);
574 timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08); 647 timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08);
575 timestamp[3] = (unsigned char)((nonce_time & 0x000000ff)); 648 timestamp[3] = (unsigned char)((nonce_time & 0x000000ff));
576 MD5Update (&md5, 649 da->update (da->ctx,
577 timestamp, 650 timestamp,
578 sizeof (timestamp)); 651 sizeof (timestamp));
579 MD5Update (&md5, 652 da->update (da->ctx,
580 (const unsigned char *) ":", 653 (const unsigned char *) ":",
581 1); 654 1);
582 MD5Update (&md5, 655 da->update (da->ctx,
583 (const unsigned char *) method, 656 (const unsigned char *) method,
584 strlen (method)); 657 strlen (method));
585 MD5Update (&md5, 658 da->update (da->ctx,
586 (const unsigned char *) ":", 659 (const unsigned char *) ":",
587 1); 660 1);
588 if (rnd_size > 0) 661 if (rnd_size > 0)
589 MD5Update (&md5, 662 da->update (da->ctx,
590 (const unsigned char *) rnd, 663 (const unsigned char *) rnd,
591 rnd_size); 664 rnd_size);
592 MD5Update (&md5, 665 da->update (da->ctx,
593 (const unsigned char *) ":", 666 (const unsigned char *) ":",
594 1); 667 1);
595 MD5Update (&md5, 668 da->update (da->ctx,
596 (const unsigned char *) uri, 669 (const unsigned char *) uri,
597 strlen (uri)); 670 strlen (uri));
598 MD5Update (&md5, 671 da->update (da->ctx,
599 (const unsigned char *) ":", 672 (const unsigned char *) ":",
600 1); 673 1);
601 MD5Update (&md5, 674 da->update (da->ctx,
602 (const unsigned char *) realm, 675 (const unsigned char *) realm,
603 strlen (realm)); 676 strlen (realm));
604 MD5Final (tmpnonce, 677 da->digest (da->ctx,
605 &md5); 678 tmpnonce);
606 cvthex (tmpnonce, 679 cvthex (tmpnonce,
607 sizeof (tmpnonce), 680 sizeof (tmpnonce),
608 nonce); 681 nonce);
@@ -713,12 +786,15 @@ check_argument_match (struct MHD_Connection *connection,
713 * Authenticates the authorization header sent by the client 786 * Authenticates the authorization header sent by the client
714 * 787 *
715 * @param connection The MHD connection structure 788 * @param connection The MHD connection structure
789 * @param da[in,out] digest algorithm to use for checking (written to as
790 * part of the calculations, but the values left in the struct
791 * are not actually expected to be useful for the caller)
716 * @param realm The realm presented to the client 792 * @param realm The realm presented to the client
717 * @param username The username needs to be authenticated 793 * @param username The username needs to be authenticated
718 * @param password The password used in the authentication 794 * @param password The password used in the authentication
719 * @param digest An optional `unsigned char *' pointer to the binary MD5 sum 795 * @param digest An optional binary hash
720 * for the precalculated hash value "username:realm:password" 796 * of the precalculated hash value "username:realm:password"
721 * of #MHD_MD5_DIGEST_SIZE bytes 797 * (must contain "da->digest_size" bytes or be NULL)
722 * @param nonce_timeout The amount of time for a nonce to be 798 * @param nonce_timeout The amount of time for a nonce to be
723 * invalid in seconds 799 * invalid in seconds
724 * @return #MHD_YES if authenticated, #MHD_NO if not, 800 * @return #MHD_YES if authenticated, #MHD_NO if not,
@@ -727,10 +803,11 @@ check_argument_match (struct MHD_Connection *connection,
727 */ 803 */
728static int 804static int
729digest_auth_check_all (struct MHD_Connection *connection, 805digest_auth_check_all (struct MHD_Connection *connection,
806 struct DigestAlgorithm *da,
730 const char *realm, 807 const char *realm,
731 const char *username, 808 const char *username,
732 const char *password, 809 const char *password,
733 const uint8_t digest[MHD_MD5_DIGEST_SIZE], 810 const uint8_t *digest,
734 unsigned int nonce_timeout) 811 unsigned int nonce_timeout)
735{ 812{
736 struct MHD_Daemon *daemon = connection->daemon; 813 struct MHD_Daemon *daemon = connection->daemon;
@@ -738,13 +815,12 @@ digest_auth_check_all (struct MHD_Connection *connection,
738 const char *header; 815 const char *header;
739 char nonce[MAX_NONCE_LENGTH]; 816 char nonce[MAX_NONCE_LENGTH];
740 char cnonce[MAX_NONCE_LENGTH]; 817 char cnonce[MAX_NONCE_LENGTH];
818 char ha1[da->digest_size * 2 + 1];
741 char qop[15]; /* auth,auth-int */ 819 char qop[15]; /* auth,auth-int */
742 char nc[20]; 820 char nc[20];
743 char response[MAX_AUTH_RESPONSE_LENGTH]; 821 char response[MAX_AUTH_RESPONSE_LENGTH];
744 const char *hentity = NULL; /* "auth-int" is not supported */ 822 const char *hentity = NULL; /* "auth-int" is not supported */
745 char ha1[HASH_MD5_HEX_LEN + 1]; 823 char noncehashexp[NONCE_STD_LEN(da->digest_size) + 1];
746 char respexp[HASH_MD5_HEX_LEN + 1];
747 char noncehashexp[NONCE_STD_LEN + 1];
748 uint32_t nonce_time; 824 uint32_t nonce_time;
749 uint32_t t; 825 uint32_t t;
750 size_t left; /* number of characters left in 'header' for 'uri' */ 826 size_t left; /* number of characters left in 'header' for 'uri' */
@@ -807,9 +883,9 @@ digest_auth_check_all (struct MHD_Connection *connection,
807 header value. */ 883 header value. */
808 return MHD_NO; 884 return MHD_NO;
809 } 885 }
810 if (TIMESTAMP_HEX_LEN != 886 if (TIMESTAMP_BIN_SIZE * 2 !=
811 MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_HEX_LEN, 887 MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
812 TIMESTAMP_HEX_LEN, 888 TIMESTAMP_BIN_SIZE * 2,
813 &nonce_time)) 889 &nonce_time))
814 { 890 {
815#ifdef HAVE_MESSAGES 891#ifdef HAVE_MESSAGES
@@ -837,6 +913,7 @@ digest_auth_check_all (struct MHD_Connection *connection,
837 daemon->digest_auth_rand_size, 913 daemon->digest_auth_rand_size,
838 connection->url, 914 connection->url,
839 realm, 915 realm,
916 da,
840 noncehashexp); 917 noncehashexp);
841 /* 918 /*
842 * Second level vetting for the nonce validity 919 * Second level vetting for the nonce validity
@@ -848,7 +925,8 @@ digest_auth_check_all (struct MHD_Connection *connection,
848 * very hard to achieve. 925 * very hard to achieve.
849 */ 926 */
850 927
851 if (0 != strcmp (nonce, noncehashexp)) 928 if (0 != strcmp (nonce,
929 noncehashexp))
852 { 930 {
853 return MHD_INVALID_NONCE; 931 return MHD_INVALID_NONCE;
854 } 932 }
@@ -908,40 +986,45 @@ digest_auth_check_all (struct MHD_Connection *connection,
908 986
909 uri = malloc (left + 1); 987 uri = malloc (left + 1);
910 if (NULL == uri) 988 if (NULL == uri)
911 { 989 {
912#ifdef HAVE_MESSAGES 990#ifdef HAVE_MESSAGES
913 MHD_DLOG(daemon, 991 MHD_DLOG(daemon,
914 _("Failed to allocate memory for auth header processing\n")); 992 _("Failed to allocate memory for auth header processing\n"));
915#endif /* HAVE_MESSAGES */ 993#endif /* HAVE_MESSAGES */
916 return MHD_NO; 994 return MHD_NO;
917 } 995 }
918 if (0 == lookup_sub_value (uri, 996 if (0 == lookup_sub_value (uri,
919 left + 1, 997 left + 1,
920 header, 998 header,
921 "uri")) 999 "uri"))
922 { 1000 {
923 free (uri); 1001 free (uri);
924 return MHD_NO; 1002 return MHD_NO;
925 } 1003 }
926
927 if (NULL != digest) 1004 if (NULL != digest)
928 { 1005 {
929 digest_calc_ha1_from_digest ("md5", 1006 /* This will initialize da->sessionkey (ha1) */
1007 digest_calc_ha1_from_digest (da->alg,
1008 da,
930 digest, 1009 digest,
931 nonce, 1010 nonce,
932 cnonce, 1011 cnonce);
933 ha1);
934 } 1012 }
935 else 1013 else
936 { 1014 {
937 digest_calc_ha1_from_user ("md5", 1015 /* This will initialize da->sessionkey (ha1) */
1016 digest_calc_ha1_from_user (da->alg,
938 username, 1017 username,
939 realm, 1018 realm,
940 password, 1019 password,
941 nonce, 1020 nonce,
942 cnonce, 1021 cnonce,
943 ha1); 1022 da);
944 } 1023 }
1024 memcpy (ha1,
1025 da->sessionkey,
1026 sizeof (ha1));
1027 /* This will initialize da->sessionkey (respexp) */
945 digest_calc_response (ha1, 1028 digest_calc_response (ha1,
946 nonce, 1029 nonce,
947 nc, 1030 nc,
@@ -950,7 +1033,7 @@ digest_auth_check_all (struct MHD_Connection *connection,
950 connection->method, 1033 connection->method,
951 uri, 1034 uri,
952 hentity, 1035 hentity,
953 respexp); 1036 da);
954 1037
955 1038
956 /* Need to unescape URI before comparing with connection->url */ 1039 /* Need to unescape URI before comparing with connection->url */
@@ -991,7 +1074,7 @@ digest_auth_check_all (struct MHD_Connection *connection,
991 } 1074 }
992 free (uri); 1075 free (uri);
993 return (0 == strcmp (response, 1076 return (0 == strcmp (response,
994 respexp)) 1077 da->sessionkey))
995 ? MHD_YES 1078 ? MHD_YES
996 : MHD_NO; 1079 : MHD_NO;
997 } 1080 }
@@ -999,7 +1082,11 @@ digest_auth_check_all (struct MHD_Connection *connection,
999 1082
1000 1083
1001/** 1084/**
1002 * Authenticates the authorization header sent by the client 1085 * Authenticates the authorization header sent by the client.
1086 * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility).
1087 * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future.
1088 * If you want to be sure you get MD5, use #MHD_digest_auth_check2
1089 * and specifiy MD5 explicitly.
1003 * 1090 *
1004 * @param connection The MHD connection structure 1091 * @param connection The MHD connection structure
1005 * @param realm The realm presented to the client 1092 * @param realm The realm presented to the client
@@ -1018,7 +1105,85 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
1018 const char *password, 1105 const char *password,
1019 unsigned int nonce_timeout) 1106 unsigned int nonce_timeout)
1020{ 1107{
1108 return MHD_digest_auth_check2 (connection,
1109 realm,
1110 username,
1111 password,
1112 nonce_timeout,
1113 MHD_DIGEST_ALG_MD5);
1114}
1115
1116
1117/**
1118 * Setup digest authentication data structures (on the
1119 * stack, hence must be done inline!). Initializes a
1120 * "struct DigestAlgorithm da" for algorithm @a algo.
1121 *
1122 * @param algo digest algorithm to provide
1123 * @param da data structure to setup
1124 */
1125#define SETUP_DA(algo,da) \
1126 union { \
1127 struct MD5Context md5; \
1128 struct sha256_ctx sha256; \
1129 } ctx; \
1130 union { \
1131 char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1132 char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1133 } skey; \
1134 struct DigestAlgorithm da; \
1135 \
1136 switch (algo) { \
1137 case MHD_DIGEST_ALG_MD5: \
1138 da.digest_size = MD5_DIGEST_SIZE; \
1139 da.ctx = &ctx.md5; \
1140 da.alg = "md5"; \
1141 da.sessionkey = skey.md5; \
1142 da.init = &MD5Init; \
1143 da.update = &MD5Update; \
1144 da.digest = &MD5Final; \
1145 break; \
1146 case MHD_DIGEST_ALG_AUTO: \
1147 /* auto == SHA256, fall-though thus intentional! */ \
1148 case MHD_DIGEST_ALG_SHA256: \
1149 da.digest_size = SHA256_DIGEST_SIZE; \
1150 da.ctx = &ctx.sha256; \
1151 da.alg = "sha-256"; \
1152 da.sessionkey = skey.sha256; \
1153 da.init = &sha256_init; \
1154 da.update = &sha256_update; \
1155 da.digest = &sha256_digest; \
1156 break; \
1157 }
1158
1159
1160
1161/**
1162 * Authenticates the authorization header sent by the client.
1163 *
1164 * @param connection The MHD connection structure
1165 * @param realm The realm presented to the client
1166 * @param username The username needs to be authenticated
1167 * @param password The password used in the authentication
1168 * @param nonce_timeout The amount of time for a nonce to be
1169 * invalid in seconds
1170 * @param algo digest algorithms allowed for verification
1171 * @return #MHD_YES if authenticated, #MHD_NO if not,
1172 * #MHD_INVALID_NONCE if nonce is invalid
1173 * @ingroup authentication
1174 */
1175_MHD_EXTERN int
1176MHD_digest_auth_check2 (struct MHD_Connection *connection,
1177 const char *realm,
1178 const char *username,
1179 const char *password,
1180 unsigned int nonce_timeout,
1181 enum MHD_DigestAuthAlgorithm algo)
1182{
1183 SETUP_DA (algo, da);
1184
1021 return digest_auth_check_all (connection, 1185 return digest_auth_check_all (connection,
1186 &da,
1022 realm, 1187 realm,
1023 username, 1188 username,
1024 password, 1189 password,
@@ -1028,7 +1193,7 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
1028 1193
1029 1194
1030/** 1195/**
1031 * Authenticates the authorization header sent by the client 1196 * Authenticates the authorization header sent by the client.
1032 * 1197 *
1033 * @param connection The MHD connection structure 1198 * @param connection The MHD connection structure
1034 * @param realm The realm presented to the client 1199 * @param realm The realm presented to the client
@@ -1036,20 +1201,29 @@ MHD_digest_auth_check (struct MHD_Connection *connection,
1036 * @param digest An `unsigned char *' pointer to the binary MD5 sum 1201 * @param digest An `unsigned char *' pointer to the binary MD5 sum
1037 * for the precalculated hash value "username:realm:password" 1202 * for the precalculated hash value "username:realm:password"
1038 * of #MHD_MD5_DIGEST_SIZE bytes 1203 * of #MHD_MD5_DIGEST_SIZE bytes
1204 * @param digest_size number of bytes in @a digest
1039 * @param nonce_timeout The amount of time for a nonce to be 1205 * @param nonce_timeout The amount of time for a nonce to be
1040 * invalid in seconds 1206 * invalid in seconds
1207 * @param algo digest algorithms allowed for verification
1041 * @return #MHD_YES if authenticated, #MHD_NO if not, 1208 * @return #MHD_YES if authenticated, #MHD_NO if not,
1042 * #MHD_INVALID_NONCE if nonce is invalid 1209 * #MHD_INVALID_NONCE if nonce is invalid
1043 * @ingroup authentication 1210 * @ingroup authentication
1044 */ 1211 */
1045_MHD_EXTERN int 1212_MHD_EXTERN int
1046MHD_digest_auth_check_digest (struct MHD_Connection *connection, 1213MHD_digest_auth_check_digest2 (struct MHD_Connection *connection,
1047 const char *realm, 1214 const char *realm,
1048 const char *username, 1215 const char *username,
1049 const uint8_t digest[MD5_DIGEST_SIZE], 1216 const uint8_t *digest,
1050 unsigned int nonce_timeout) 1217 size_t digest_size,
1218 unsigned int nonce_timeout,
1219 enum MHD_DigestAuthAlgorithm algo)
1051{ 1220{
1221 SETUP_DA (algo, da);
1222
1223 if (da.digest_size != digest_size)
1224 MHD_PANIC (_("digest size missmatch")); /* API violation! */
1052 return digest_auth_check_all (connection, 1225 return digest_auth_check_all (connection,
1226 &da,
1053 realm, 1227 realm,
1054 username, 1228 username,
1055 NULL, 1229 NULL,
@@ -1059,6 +1233,40 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection,
1059 1233
1060 1234
1061/** 1235/**
1236 * Authenticates the authorization header sent by the client.
1237 * Uses #MHD_DIGEST_ALG_MD5 (required, as @a digest is of fixed
1238 * size).
1239 *
1240 * @param connection The MHD connection structure
1241 * @param realm The realm presented to the client
1242 * @param username The username needs to be authenticated
1243 * @param digest An `unsigned char *' pointer to the binary digest
1244 * for the precalculated hash value "username:realm:password"
1245 * of @a digest_size bytes
1246 * @param nonce_timeout The amount of time for a nonce to be
1247 * invalid in seconds
1248 * @return #MHD_YES if authenticated, #MHD_NO if not,
1249 * #MHD_INVALID_NONCE if nonce is invalid
1250 * @ingroup authentication
1251 */
1252_MHD_EXTERN int
1253MHD_digest_auth_check_digest (struct MHD_Connection *connection,
1254 const char *realm,
1255 const char *username,
1256 const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1257 unsigned int nonce_timeout)
1258{
1259 return MHD_digest_auth_check_digest2 (connection,
1260 realm,
1261 username,
1262 digest,
1263 MHD_MD5_DIGEST_SIZE,
1264 nonce_timeout,
1265 MHD_DIGEST_ALG_MD5);
1266}
1267
1268
1269/**
1062 * Queues a response to request authentication from the client 1270 * Queues a response to request authentication from the client
1063 * 1271 *
1064 * @param connection The MHD connection structure 1272 * @param connection The MHD connection structure
@@ -1069,81 +1277,90 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection,
1069 * header and that the caller should not do this 1277 * header and that the caller should not do this
1070 * @param signal_stale #MHD_YES if the nonce is invalid to add 1278 * @param signal_stale #MHD_YES if the nonce is invalid to add
1071 * 'stale=true' to the authentication header 1279 * 'stale=true' to the authentication header
1280 * @param algo digest algorithm to use
1072 * @return #MHD_YES on success, #MHD_NO otherwise 1281 * @return #MHD_YES on success, #MHD_NO otherwise
1073 * @ingroup authentication 1282 * @ingroup authentication
1074 */ 1283 */
1075int 1284int
1076MHD_queue_auth_fail_response (struct MHD_Connection *connection, 1285MHD_queue_auth_fail_response2 (struct MHD_Connection *connection,
1077 const char *realm, 1286 const char *realm,
1078 const char *opaque, 1287 const char *opaque,
1079 struct MHD_Response *response, 1288 struct MHD_Response *response,
1080 int signal_stale) 1289 int signal_stale,
1290 enum MHD_DigestAuthAlgorithm algo)
1081{ 1291{
1082 int ret; 1292 int ret;
1083 int hlen; 1293 int hlen;
1084 char nonce[NONCE_STD_LEN + 1]; 1294 SETUP_DA (algo, da);
1085 1295
1086 /* Generating the server nonce */ 1296 {
1087 calculate_nonce ((uint32_t) MHD_monotonic_sec_counter(), 1297 char nonce[NONCE_STD_LEN(da.digest_size) + 1];
1088 connection->method, 1298 /* Generating the server nonce */
1089 connection->daemon->digest_auth_random, 1299 calculate_nonce ((uint32_t) MHD_monotonic_sec_counter(),
1090 connection->daemon->digest_auth_rand_size, 1300 connection->method,
1091 connection->url, 1301 connection->daemon->digest_auth_random,
1092 realm, 1302 connection->daemon->digest_auth_rand_size,
1093 nonce); 1303 connection->url,
1094 if (MHD_YES != 1304 realm,
1095 check_nonce_nc (connection, 1305 &da,
1096 nonce, 1306 nonce);
1097 0)) 1307 if (MHD_YES !=
1098 { 1308 check_nonce_nc (connection,
1309 nonce,
1310 0))
1311 {
1099#ifdef HAVE_MESSAGES 1312#ifdef HAVE_MESSAGES
1100 MHD_DLOG (connection->daemon, 1313 MHD_DLOG (connection->daemon,
1101 _("Could not register nonce (is the nonce array size zero?).\n")); 1314 _("Could not register nonce (is the nonce array size zero?).\n"));
1102#endif 1315#endif
1103 return MHD_NO; 1316 return MHD_NO;
1104 } 1317 }
1105 /* Building the authentication header */ 1318 /* Building the authentication header */
1106 hlen = MHD_snprintf_ (NULL, 1319 hlen = MHD_snprintf_ (NULL,
1107 0, 1320 0,
1108 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s", 1321 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1109 realm, 1322 realm,
1110 nonce, 1323 nonce,
1111 opaque, 1324 opaque,
1112 signal_stale 1325 da.alg,
1113 ? ",stale=\"true\"" 1326 signal_stale
1114 : ""); 1327 ? ",stale=\"true\""
1115 if (hlen > 0) 1328 : "");
1116 { 1329 if (hlen > 0)
1117 char *header; 1330 {
1331 char *header;
1118 1332
1119 header = MHD_calloc_ (1, hlen + 1); 1333 header = MHD_calloc_ (1,
1120 if (NULL == header) 1334 hlen + 1);
1121 { 1335 if (NULL == header)
1336 {
1122#ifdef HAVE_MESSAGES 1337#ifdef HAVE_MESSAGES
1123 MHD_DLOG(connection->daemon, 1338 MHD_DLOG(connection->daemon,
1124 _("Failed to allocate memory for auth response header\n")); 1339 _("Failed to allocate memory for auth response header\n"));
1125#endif /* HAVE_MESSAGES */ 1340#endif /* HAVE_MESSAGES */
1126 return MHD_NO; 1341 return MHD_NO;
1127 } 1342 }
1128 1343
1129 if (MHD_snprintf_ (header, 1344 if (MHD_snprintf_ (header,
1130 hlen + 1, 1345 hlen + 1,
1131 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s", 1346 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1132 realm, 1347 realm,
1133 nonce, 1348 nonce,
1134 opaque, 1349 opaque,
1135 signal_stale 1350 da.alg,
1136 ? ",stale=\"true\"" 1351 signal_stale
1137 : "") == hlen) 1352 ? ",stale=\"true\""
1138 ret = MHD_add_response_header(response, 1353 : "") == hlen)
1139 MHD_HTTP_HEADER_WWW_AUTHENTICATE, 1354 ret = MHD_add_response_header(response,
1140 header); 1355 MHD_HTTP_HEADER_WWW_AUTHENTICATE,
1141 else 1356 header);
1142 ret = MHD_NO; 1357 else
1143 free (header); 1358 ret = MHD_NO;
1144 } 1359 free (header);
1145 else 1360 }
1146 ret = MHD_NO; 1361 else
1362 ret = MHD_NO;
1363 }
1147 1364
1148 if (MHD_YES == ret) 1365 if (MHD_YES == ret)
1149 { 1366 {
@@ -1162,4 +1379,36 @@ MHD_queue_auth_fail_response (struct MHD_Connection *connection,
1162} 1379}
1163 1380
1164 1381
1382/**
1383 * Queues a response to request authentication from the client.
1384 * For now uses MD5 (for backwards-compatibility). Still, if you
1385 * need to be sure, use #MHD_queue_fail_auth_response2().
1386 *
1387 * @param connection The MHD connection structure
1388 * @param realm the realm presented to the client
1389 * @param opaque string to user for opaque value
1390 * @param response reply to send; should contain the "access denied"
1391 * body; note that this function will set the "WWW Authenticate"
1392 * header and that the caller should not do this
1393 * @param signal_stale #MHD_YES if the nonce is invalid to add
1394 * 'stale=true' to the authentication header
1395 * @return #MHD_YES on success, #MHD_NO otherwise
1396 * @ingroup authentication
1397 */
1398int
1399MHD_queue_auth_fail_response (struct MHD_Connection *connection,
1400 const char *realm,
1401 const char *opaque,
1402 struct MHD_Response *response,
1403 int signal_stale)
1404{
1405 return MHD_queue_auth_fail_response2 (connection,
1406 realm,
1407 opaque,
1408 response,
1409 signal_stale,
1410 MHD_DIGEST_ALG_MD5);
1411}
1412
1413
1165/* end of digestauth.c */ 1414/* end of digestauth.c */