]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/digest/auth_digest.cc
Document the 'carp' cache_peer option
[thirdparty/squid.git] / src / auth / digest / auth_digest.cc
CommitLineData
2d70df72 1
2/*
2f44bd34 3 * $Id: auth_digest.cc,v 1.20 2003/02/06 09:57:40 robertc Exp $
2d70df72 4 *
5 * DEBUG: section 29 Authenticator
6 * AUTHOR: Robert Collins
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36/* The functions in this file handle authentication.
37 * They DO NOT perform access control or auditing.
38 * See acl.c for access control and client_side.c for auditing */
39
40
41#include "squid.h"
42#include "rfc2617.h"
43#include "auth_digest.h"
e6ccf245 44#include "authenticate.h"
45#include "Store.h"
2d70df72 46
c78aa667 47extern AUTHSSETUP authSchemeSetup_digest;
48
2d70df72 49static void
e6ccf245 50authenticateStateFree(DigestAuthenticateStateData * r)
2d70df72 51{
52 cbdataFree(r);
53}
54
55/* Digest Scheme */
56
57static HLPCB authenticateDigestHandleReply;
58static AUTHSACTIVE authenticateDigestActive;
59static AUTHSADDHEADER authDigestAddHeader;
60#if WAITING_FOR_TE
61static AUTHSADDTRAILER authDigestAddTrailer;
62#endif
63static AUTHSAUTHED authDigestAuthenticated;
64static AUTHSAUTHUSER authenticateDigestAuthenticateUser;
65static AUTHSCONFIGURED authDigestConfigured;
66static AUTHSDIRECTION authenticateDigestDirection;
67static AUTHSDECODE authenticateDigestDecodeAuth;
68static AUTHSDUMP authDigestCfgDump;
69static AUTHSFIXERR authenticateDigestFixHeader;
70static AUTHSFREE authenticateDigestUserFree;
71static AUTHSFREECONFIG authDigestFreeConfig;
72static AUTHSINIT authDigestInit;
73static AUTHSPARSE authDigestParse;
74static AUTHSREQFREE authDigestAURequestFree;
75static AUTHSSTART authenticateDigestStart;
76static AUTHSSTATS authenticateDigestStats;
77static AUTHSUSERNAME authenticateDigestUsername;
78static AUTHSSHUTDOWN authDigestDone;
79
80static helper *digestauthenticators = NULL;
81
82static hash_table *digest_nonce_cache;
83
84static auth_digest_config *digestConfig = NULL;
85
86static int authdigest_initialised = 0;
c78aa667 87static MemPool *digest_user_pool = NULL;
88static MemPool *digest_request_pool = NULL;
89static MemPool *digest_nonce_pool = NULL;
2d70df72 90
e6ccf245 91CBDATA_TYPE(DigestAuthenticateStateData);
2d70df72 92
93/*
94 *
95 * Nonce Functions
96 *
97 */
98
99static void authenticateDigestNonceCacheCleanup(void *data);
100static digest_nonce_h *authenticateDigestNonceFindNonce(const char *nonceb64);
c193c972 101static digest_nonce_h *authenticateDigestNonceNew(void);
c78aa667 102static void authenticateDigestNonceDelete(digest_nonce_h * nonce);
c193c972 103static void authenticateDigestNonceSetup(void);
104static void authenticateDigestNonceShutdown(void);
105static void authenticateDigestNonceReconfigure(void);
c78aa667 106static const char *authenticateDigestNonceNonceb64(digest_nonce_h * nonce);
107static int authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9]);
108static int authDigestNonceIsStale(digest_nonce_h * nonce);
109static void authDigestNonceEncode(digest_nonce_h * nonce);
110static int authDigestNonceLastRequest(digest_nonce_h * nonce);
111static void authDigestNonceLink(digest_nonce_h * nonce);
112static void authDigestNonceUnlink(digest_nonce_h * nonce);
113#if NOT_USED
114static int authDigestNonceLinks(digest_nonce_h * nonce);
115#endif
116static void authDigestNonceUserUnlink(digest_nonce_h * nonce);
117static void authDigestNoncePurge(digest_nonce_h * nonce);
2d70df72 118
c78aa667 119static void
2d70df72 120authDigestNonceEncode(digest_nonce_h * nonce)
121{
122 if (!nonce)
123 return;
4a8b20e8 124 if (nonce->key)
125 xfree(nonce->key);
126 nonce->key = xstrdup(base64_encode_bin((char *) &(nonce->noncedata), sizeof(digest_nonce_data)));
2d70df72 127}
128
c78aa667 129static digest_nonce_h *
c193c972 130authenticateDigestNonceNew(void)
2d70df72 131{
2f44bd34 132 digest_nonce_h *newnonce = static_cast < digest_nonce_h * >(memPoolAlloc(digest_nonce_pool));
2d70df72 133 digest_nonce_h *temp;
134
135/* NONCE CREATION - NOTES AND REASONING. RBC 20010108
136 * === EXCERPT FROM RFC 2617 ===
137 * The contents of the nonce are implementation dependent. The quality
138 * of the implementation depends on a good choice. A nonce might, for
139 * example, be constructed as the base 64 encoding of
140 *
141 * time-stamp H(time-stamp ":" ETag ":" private-key)
142 *
143 * where time-stamp is a server-generated time or other non-repeating
144 * value, ETag is the value of the HTTP ETag header associated with
145 * the requested entity, and private-key is data known only to the
146 * server. With a nonce of this form a server would recalculate the
147 * hash portion after receiving the client authentication header and
148 * reject the request if it did not match the nonce from that header
149 * or if the time-stamp value is not recent enough. In this way the
150 * server can limit the time of the nonce's validity. The inclusion of
151 * the ETag prevents a replay request for an updated version of the
152 * resource. (Note: including the IP address of the client in the
153 * nonce would appear to offer the server the ability to limit the
154 * reuse of the nonce to the same client that originally got it.
155 * However, that would break proxy farms, where requests from a single
156 * user often go through different proxies in the farm. Also, IP
157 * address spoofing is not that hard.)
158 * ====
159 *
160 * Now for my reasoning:
74830fc8 161 * We will not accept a unrecognised nonce->we have all recognisable
162 * nonces stored If we send out unique base64 encodings we guarantee
163 * that a given nonce applies to only one user (barring attacks or
164 * really bad timing with expiry and creation). Using a random
165 * component in the nonce allows us to loop to find a unique nonce.
2d70df72 166 * We use H(nonce_data) so the nonce is meaningless to the reciever.
167 * So our nonce looks like base64(H(timestamp,pointertohash,randomdata))
74830fc8 168 * And even if our randomness is not very random (probably due to
169 * bad coding on my part) we don't really care - the timestamp and
170 * memory pointer should provide enough protection for the users
171 * authentication.
2d70df72 172 */
173
174 /* create a new nonce */
175 newnonce->nc = 0;
176 newnonce->flags.valid = 1;
177 newnonce->noncedata.self = newnonce;
178 newnonce->noncedata.creationtime = current_time.tv_sec;
b600a19d 179 newnonce->noncedata.randomdata = squid_random();
2d70df72 180
181 authDigestNonceEncode(newnonce);
74830fc8 182 /*
183 * loop until we get a unique nonce. The nonce creation must
184 * have a random factor
185 */
2f44bd34 186 while ((temp = authenticateDigestNonceFindNonce((char const *) (newnonce->key)))) {
2d70df72 187 /* create a new nonce */
b600a19d 188 newnonce->noncedata.randomdata = squid_random();
2d70df72 189 authDigestNonceEncode(newnonce);
190 }
4a8b20e8 191 hash_join(digest_nonce_cache, newnonce);
2d70df72 192 /* the cache's link */
193 authDigestNonceLink(newnonce);
194 newnonce->flags.incache = 1;
2f44bd34 195 debug(29, 5) ("authenticateDigestNonceNew: created nonce %p at %ld\n", newnonce, (long int) newnonce->noncedata.creationtime);
2d70df72 196 return newnonce;
197}
198
c78aa667 199static void
2d70df72 200authenticateDigestNonceDelete(digest_nonce_h * nonce)
201{
202 if (nonce) {
203 assert(nonce->references == 0);
204#if UNREACHABLECODE
205 if (nonce->flags.incache)
4a8b20e8 206 hash_remove_link(digest_nonce_cache, nonce);
2d70df72 207#endif
208 assert(nonce->flags.incache == 0);
4a8b20e8 209 safe_free(nonce->key);
2d70df72 210 memPoolFree(digest_nonce_pool, nonce);
211 }
212}
213
c78aa667 214static void
c193c972 215authenticateDigestNonceSetup(void)
2d70df72 216{
217 if (!digest_nonce_pool)
218 digest_nonce_pool = memPoolCreate("Digest Scheme nonce's", sizeof(digest_nonce_h));
219 if (!digest_nonce_cache) {
220 digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
221 assert(digest_nonce_cache);
222 eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig->nonceGCInterval, 1);
223 }
224}
225
c78aa667 226static void
c193c972 227authenticateDigestNonceShutdown(void)
2d70df72 228{
229 /*
230 * We empty the cache of any nonces left in there.
231 */
232 digest_nonce_h *nonce;
233 if (digest_nonce_cache) {
234 debug(29, 2) ("authenticateDigestNonceShutdown: Shutting down nonce cache \n");
235 hash_first(digest_nonce_cache);
236 while ((nonce = ((digest_nonce_h *) hash_next(digest_nonce_cache)))) {
237 assert(nonce->flags.incache);
238 authDigestNoncePurge(nonce);
239 }
240 }
2f44bd34 241#if DEBUGSHUTDOWN
2d70df72 242 if (digest_nonce_pool) {
d96ceb8e 243 memPoolDestroy(&digest_nonce_pool);
2d70df72 244 }
2f44bd34 245#endif
2d70df72 246 debug(29, 2) ("authenticateDigestNonceShutdown: Nonce cache shutdown\n");
247}
248
c78aa667 249static void
c193c972 250authenticateDigestNonceReconfigure(void)
2d70df72 251{
252}
253
c78aa667 254static void
2d70df72 255authenticateDigestNonceCacheCleanup(void *data)
256{
257 /*
74830fc8 258 * We walk the hash by nonceb64 as that is the unique key we
259 * use. For big hash tables we could consider stepping through
260 * the cache, 100/200 entries at a time. Lets see how it flies
261 * first.
2d70df72 262 */
263 digest_nonce_h *nonce;
264 debug(29, 3) ("authenticateDigestNonceCacheCleanup: Cleaning the nonce cache now\n");
33272404 265 debug(29, 3) ("authenticateDigestNonceCacheCleanup: Current time: %ld\n",
2f44bd34 266 (long int) current_time.tv_sec);
2d70df72 267 hash_first(digest_nonce_cache);
268 while ((nonce = ((digest_nonce_h *) hash_next(digest_nonce_cache)))) {
2f44bd34 269 debug(29, 3) ("authenticateDigestNonceCacheCleanup: nonce entry : %p '%s'\n", nonce, (char *) nonce->key);
270 debug(29, 4) ("authenticateDigestNonceCacheCleanup: Creation time: %ld\n", (long int) nonce->noncedata.creationtime);
2d70df72 271 if (authDigestNonceIsStale(nonce)) {
2f44bd34 272 debug(29, 4) ("authenticateDigestNonceCacheCleanup: Removing nonce %s from cache due to timeout.\n", (char *) nonce->key);
2d70df72 273 assert(nonce->flags.incache);
274 /* invalidate nonce so future requests fail */
275 nonce->flags.valid = 0;
276 /* if it is tied to a auth_user, remove the tie */
277 authDigestNonceUserUnlink(nonce);
278 authDigestNoncePurge(nonce);
279 }
280 }
281 debug(29, 3) ("authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.\n");
282 if (authenticateDigestActive())
283 eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, digestConfig->nonceGCInterval, 1);
284}
285
c78aa667 286static void
2d70df72 287authDigestNonceLink(digest_nonce_h * nonce)
288{
289 assert(nonce != NULL);
290 nonce->references++;
33272404 291 debug(29, 9) ("authDigestNonceLink: nonce '%p' now at '%d'.\n", nonce, nonce->references);
2d70df72 292}
293
c78aa667 294#if NOT_USED
295static int
2d70df72 296authDigestNonceLinks(digest_nonce_h * nonce)
297{
298 if (!nonce)
299 return -1;
300 return nonce->references;
301}
c78aa667 302#endif
2d70df72 303
c78aa667 304static void
2d70df72 305authDigestNonceUnlink(digest_nonce_h * nonce)
306{
307 assert(nonce != NULL);
308 if (nonce->references > 0) {
309 nonce->references--;
310 } else {
33272404 311 debug(29, 1) ("authDigestNonceUnlink; Attempt to lower nonce %p refcount below 0!\n", nonce);
2d70df72 312 }
33272404 313 debug(29, 9) ("authDigestNonceUnlink: nonce '%p' now at '%d'.\n", nonce, nonce->references);
2d70df72 314 if (nonce->references == 0)
315 authenticateDigestNonceDelete(nonce);
316}
317
c78aa667 318static const char *
2d70df72 319authenticateDigestNonceNonceb64(digest_nonce_h * nonce)
320{
321 if (!nonce)
322 return NULL;
2f44bd34 323 return (char const *) nonce->key;
2d70df72 324}
325
c78aa667 326static digest_nonce_h *
2d70df72 327authenticateDigestNonceFindNonce(const char *nonceb64)
328{
329 digest_nonce_h *nonce = NULL;
330 if (nonceb64 == NULL)
331 return NULL;
332 debug(29, 9) ("authDigestNonceFindNonce:looking for nonceb64 '%s' in the nonce cache.\n", nonceb64);
2f44bd34 333 nonce = static_cast < digest_nonce_h * >(hash_lookup(digest_nonce_cache, nonceb64));
e6ccf245 334 if ((nonce == NULL) || (strcmp(authenticateDigestNonceNonceb64(nonce), nonceb64)))
2d70df72 335 return NULL;
33272404 336 debug(29, 9) ("authDigestNonceFindNonce: Found nonce '%p'\n", nonce);
2d70df72 337 return nonce;
338}
339
c78aa667 340static int
2d70df72 341authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
342{
d205783b 343 unsigned long intnc;
2d70df72 344 /* do we have a nonce ? */
345 if (!nonce)
346 return 0;
d205783b 347 intnc = strtol(nc, NULL, 16);
348 if ((digestConfig->NonceStrictness && intnc != nonce->nc + 1) ||
349 intnc < nonce->nc + 1) {
2d70df72 350 debug(29, 4) ("authDigestNonceIsValid: Nonce count doesn't match\n");
351 nonce->flags.valid = 0;
352 return 0;
353 }
354 /* has it already been invalidated ? */
355 if (!nonce->flags.valid) {
356 debug(29, 4) ("authDigestNonceIsValid: Nonce already invalidated\n");
357 return 0;
358 }
359 /* seems ok */
d205783b 360 /* increment the nonce count - we've already checked that intnc is a
361 * valid representation for us, so we don't need the test here.
362 */
363 nonce->nc = intnc;
2d70df72 364 return -1;
365}
366
c78aa667 367static int
2d70df72 368authDigestNonceIsStale(digest_nonce_h * nonce)
369{
370 /* do we have a nonce ? */
371 if (!nonce)
372 return -1;
373 /* has it's max duration expired? */
374 if (nonce->noncedata.creationtime + digestConfig->noncemaxduration < current_time.tv_sec) {
2f44bd34 375 debug(29, 4) ("authDigestNonceIsStale: Nonce is too old. %ld %d %ld\n", (long int) nonce->noncedata.creationtime, (int) digestConfig->noncemaxduration, (long int) current_time.tv_sec);
2d70df72 376 nonce->flags.valid = 0;
377 return -1;
378 }
379 if (nonce->nc > 99999998) {
380 debug(29, 4) ("authDigestNonceIsStale: Nonce count overflow\n");
381 nonce->flags.valid = 0;
382 return -1;
383 }
384 if (nonce->nc > digestConfig->noncemaxuses) {
385 debug(29, 4) ("authDigestNoncelastRequest: Nonce count over user limit\n");
386 nonce->flags.valid = 0;
387 return -1;
388 }
389 /* seems ok */
390 return 0;
391}
392
2f44bd34 393 /* return -1 if the digest will be stale on the next request */
c78aa667 394static int
2d70df72 395authDigestNonceLastRequest(digest_nonce_h * nonce)
396{
397 if (!nonce)
398 return -1;
399 if (nonce->nc == 99999997) {
400 debug(29, 4) ("authDigestNoncelastRequest: Nonce count about to overflow\n");
401 return -1;
402 }
d205783b 403 if (nonce->nc >= digestConfig->noncemaxuses - 1) {
2d70df72 404 debug(29, 4) ("authDigestNoncelastRequest: Nonce count about to hit user limit\n");
405 return -1;
406 }
407 /* and other tests are possible. */
408 return 0;
409}
410
c78aa667 411static void
2d70df72 412authDigestNoncePurge(digest_nonce_h * nonce)
413{
414 if (!nonce)
415 return;
416 if (!nonce->flags.incache)
417 return;
4a8b20e8 418 hash_remove_link(digest_nonce_cache, nonce);
2d70df72 419 nonce->flags.incache = 0;
420 /* the cache's link */
421 authDigestNonceUnlink(nonce);
422}
423
2f44bd34 424 /* USER related functions */
2d70df72 425
426
c78aa667 427#if NOT_USED
428static int
2d70df72 429authDigestUsercmpname(digest_user_h * u1, digest_user_h * u2)
430{
431 return strcmp(u1->username, u2->username);
432}
c78aa667 433#endif
2d70df72 434
c78aa667 435static auth_user_t *
2d70df72 436authDigestUserFindUsername(const char *username)
437{
438 auth_user_hash_pointer *usernamehash;
439 auth_user_t *auth_user;
440 debug(29, 9) ("authDigestUserFindUsername: Looking for user '%s'\n", username);
2f44bd34 441 if (username && (usernamehash = static_cast < auth_user_hash_pointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
e6ccf245 442 while ((authUserHashPointerUser(usernamehash)->auth_type != AUTH_DIGEST) &&
2d70df72 443 (usernamehash->next))
2f44bd34 444 usernamehash = static_cast < auth_user_hash_pointer * >(usernamehash->next);
2d70df72 445 auth_user = NULL;
e6ccf245 446 if (authUserHashPointerUser(usernamehash)->auth_type == AUTH_DIGEST) {
447 auth_user = authUserHashPointerUser(usernamehash);
2d70df72 448 }
449 return auth_user;
450 }
451 return NULL;
452}
453
c78aa667 454static digest_user_h *
c193c972 455authDigestUserNew(void)
2d70df72 456{
2f44bd34 457 return static_cast < digest_user_h * >(memPoolAlloc(digest_user_pool));
2d70df72 458}
459
c78aa667 460static void
c193c972 461authDigestUserSetup(void)
2d70df72 462{
463 if (!digest_user_pool)
464 digest_user_pool = memPoolCreate("Digest Scheme User Data", sizeof(digest_user_h));
465}
466
c78aa667 467static void
c193c972 468authDigestUserShutdown(void)
2d70df72 469{
470 /*
471 * Future work: the auth framework could flush it's cache
472 */
473 auth_user_hash_pointer *usernamehash;
474 auth_user_t *auth_user;
475 hash_first(proxy_auth_username_cache);
476 while ((usernamehash = ((auth_user_hash_pointer *) hash_next(proxy_auth_username_cache)))) {
e6ccf245 477 auth_user = authUserHashPointerUser(usernamehash);
60d096f4 478 if (authscheme_list[auth_user->auth_module - 1].typestr &&
479 strcmp(authscheme_list[auth_user->auth_module - 1].typestr, "digest") == 0)
2d70df72 480 /* it's digest */
481 authenticateAuthUserUnlock(auth_user);
482 }
2f44bd34 483#if DEBUGSHUTDOWN
2d70df72 484 if (digest_user_pool) {
d96ceb8e 485 memPoolDestroy(&digest_user_pool);
2d70df72 486 }
2f44bd34 487#endif
2d70df72 488}
489
490
491/* request related functions */
492
493/* delete the digest reuqest structure. Does NOT delete related structures */
c78aa667 494static void
2d70df72 495authDigestRequestDelete(digest_request_h * digest_request)
496{
497 if (digest_request->nonceb64)
498 xfree(digest_request->nonceb64);
499 if (digest_request->cnonce)
500 xfree(digest_request->cnonce);
501 if (digest_request->realm)
502 xfree(digest_request->realm);
503 if (digest_request->pszPass)
504 xfree(digest_request->pszPass);
505 if (digest_request->algorithm)
506 xfree(digest_request->algorithm);
507 if (digest_request->pszMethod)
508 xfree(digest_request->pszMethod);
509 if (digest_request->qop)
510 xfree(digest_request->qop);
511 if (digest_request->uri)
512 xfree(digest_request->uri);
513 if (digest_request->response)
514 xfree(digest_request->response);
515 if (digest_request->nonce)
516 authDigestNonceUnlink(digest_request->nonce);
517 memPoolFree(digest_request_pool, digest_request);
518}
519
c78aa667 520static void
2d70df72 521authDigestAURequestFree(auth_user_request_t * auth_user_request)
522{
523 if (auth_user_request->scheme_data != NULL)
2f44bd34 524 authDigestRequestDelete(static_cast < digest_request_h * >(auth_user_request->scheme_data));
2d70df72 525}
526
c78aa667 527static digest_request_h *
c193c972 528authDigestRequestNew(void)
2d70df72 529{
530 digest_request_h *tmp;
2f44bd34 531 tmp = static_cast < digest_request_h * >(memPoolAlloc(digest_request_pool));
2d70df72 532 assert(tmp != NULL);
533 return tmp;
534}
535
c78aa667 536static void
c193c972 537authDigestRequestSetup(void)
2d70df72 538{
539 if (!digest_request_pool)
540 digest_request_pool = memPoolCreate("Digest Scheme Request Data", sizeof(digest_request_h));
541}
542
c78aa667 543static void
c193c972 544authDigestRequestShutdown(void)
2d70df72 545{
546 /* No requests should be in progress when we get here */
2f44bd34 547#if DEBUGSHUTDOWN
2d70df72 548 if (digest_request_pool) {
d96ceb8e 549 memPoolDestroy(&digest_request_pool);
2d70df72 550 }
2f44bd34 551#endif
2d70df72 552}
553
554
c78aa667 555static void
2d70df72 556authDigestDone(void)
557{
558 if (digestauthenticators)
559 helperShutdown(digestauthenticators);
560 authdigest_initialised = 0;
561 if (!shutting_down) {
562 authenticateDigestNonceReconfigure();
563 return;
564 }
565 if (digestauthenticators) {
566 helperFree(digestauthenticators);
567 digestauthenticators = NULL;
568 }
569 authDigestRequestShutdown();
570 authDigestUserShutdown();
571 authenticateDigestNonceShutdown();
572 debug(29, 2) ("authenticateDigestDone: Digest authentication shut down.\n");
573}
574
575static void
576authDigestCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
577{
2f44bd34 578 auth_digest_config *config = static_cast < auth_digest_config * >(scheme->scheme_data);
2d70df72 579 wordlist *list = config->authenticate;
580 debug(29, 9) ("authDigestCfgDump: Dumping configuration\n");
581 storeAppendPrintf(entry, "%s %s", name, "digest");
582 while (list != NULL) {
583 storeAppendPrintf(entry, " %s", list->key);
584 list = list->next;
585 }
586 storeAppendPrintf(entry, "\n%s %s realm %s\n%s %s children %d\n%s %s nonce_max_count %d\n%s %s nonce_max_duration %d seconds\n%s %s nonce_garbage_interval %d seconds\n",
587 name, "digest", config->digestAuthRealm,
588 name, "digest", config->authenticateChildren,
589 name, "digest", config->noncemaxuses,
2f44bd34 590 name, "digest", (int) config->noncemaxduration,
591 name, "digest", (int) config->nonceGCInterval);
2d70df72 592}
593
594void
595authSchemeSetup_digest(authscheme_entry_t * authscheme)
596{
597 assert(!authdigest_initialised);
598 authscheme->Active = authenticateDigestActive;
599 authscheme->configured = authDigestConfigured;
600 authscheme->parse = authDigestParse;
601 authscheme->freeconfig = authDigestFreeConfig;
602 authscheme->dump = authDigestCfgDump;
603 authscheme->init = authDigestInit;
604 authscheme->authAuthenticate = authenticateDigestAuthenticateUser;
605 authscheme->authenticated = authDigestAuthenticated;
606 authscheme->authFixHeader = authenticateDigestFixHeader;
607 authscheme->FreeUser = authenticateDigestUserFree;
608 authscheme->AddHeader = authDigestAddHeader;
609#if WAITING_FOR_TE
610 authscheme->AddTrailer = authDigestAddTrailer;
611#endif
612 authscheme->authStart = authenticateDigestStart;
613 authscheme->authStats = authenticateDigestStats;
614 authscheme->authUserUsername = authenticateDigestUsername;
615 authscheme->getdirection = authenticateDigestDirection;
616 authscheme->oncloseconnection = NULL;
617 authscheme->decodeauth = authenticateDigestDecodeAuth;
618 authscheme->donefunc = authDigestDone;
619 authscheme->requestFree = authDigestAURequestFree;
60d096f4 620 authscheme->authConnLastHeader = NULL;
2d70df72 621}
622
c78aa667 623static int
c193c972 624authenticateDigestActive(void)
2d70df72 625{
626 return (authdigest_initialised == 1) ? 1 : 0;
627}
c78aa667 628static int
c193c972 629authDigestConfigured(void)
2d70df72 630{
631 if ((digestConfig != NULL) && (digestConfig->authenticate != NULL) &&
632 (digestConfig->authenticateChildren != 0) &&
633 (digestConfig->digestAuthRealm != NULL) && (digestConfig->noncemaxduration > -1))
634 return 1;
635 return 0;
636}
637
c78aa667 638static int
2d70df72 639authDigestAuthenticated(auth_user_request_t * auth_user_request)
640{
2f44bd34 641 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
bd507204 642 if (digest_user->flags.credentials_ok == 1)
2d70df72 643 return 1;
644 else
645 return 0;
646}
647
648/* log a digest user in
649 */
650static void
651authenticateDigestAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
652{
653 auth_user_t *auth_user;
654 digest_request_h *digest_request;
655 digest_user_h *digest_user;
656
657 HASHHEX SESSIONKEY;
658 HASHHEX HA2 = "";
659 HASHHEX Response;
660
661 assert(auth_user_request->auth_user != NULL);
662 auth_user = auth_user_request->auth_user;
663
2d70df72 664 assert(auth_user->scheme_data != NULL);
2f44bd34 665 digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 666
9bea1d5b 667 /* if the check has corrupted the user, just return */
bd507204 668 if (digest_user->flags.credentials_ok == 3) {
669 return;
670 }
2d70df72 671 assert(auth_user_request->scheme_data != NULL);
2f44bd34 672 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
2d70df72 673
674 /* do we have the HA1 */
675 if (!digest_user->HA1created) {
bd507204 676 digest_user->flags.credentials_ok = 2;
2d70df72 677 return;
678 }
679 if (digest_request->nonce == NULL) {
680 /* this isn't a nonce we issued */
681 /* TODO: record breaks in authentication at the request level
74830fc8 682 * This is probably best done with support changes at the
683 * auth_rewrite level -RBC
2d70df72 684 * and can wait for auth_rewrite V2.
74830fc8 685 * RBC 20010902 further note: flags.credentials ok is now
686 * a local scheme flag, so we can move this to the request
687 * level at any time.
2d70df72 688 */
bd507204 689 digest_user->flags.credentials_ok = 3;
2d70df72 690 return;
691 }
692 DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
693 authenticateDigestNonceNonceb64(digest_request->nonce),
694 digest_request->cnonce,
695 digest_user->HA1, SESSIONKEY);
696 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
697 digest_request->nc, digest_request->cnonce, digest_request->qop,
698 RequestMethodStr[request->method], digest_request->uri, HA2, Response);
699
700 debug(29, 9) ("\nResponse = '%s'\n"
701 "squid is = '%s'\n", digest_request->response, Response);
702
703 if (strcasecmp(digest_request->response, Response)) {
bd507204 704 digest_user->flags.credentials_ok = 3;
2d70df72 705 return;
706 }
bd507204 707 digest_user->flags.credentials_ok = 1;
2d70df72 708 /* password was checked and did match */
709 debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n",
710 digest_user->username);
711
712 /* auth_user is now linked, we reset these values
713 * after external auth occurs anyway */
714 auth_user->expiretime = current_time.tv_sec;
2d70df72 715 return;
716}
717
c78aa667 718static int
2d70df72 719authenticateDigestDirection(auth_user_request_t * auth_user_request)
720{
721 digest_request_h *digest_request;
2f44bd34 722 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
bd507204 723 /* null auth_user is checked for by authenticateDirection */
724 switch (digest_user->flags.credentials_ok) {
2d70df72 725 case 0: /* not checked */
726 return -1;
727 case 1: /* checked & ok */
2f44bd34 728 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
2d70df72 729 if (authDigestNonceIsStale(digest_request->nonce))
730 /* send stale response to the client agent */
731 return -2;
732 return 0;
733 case 2: /* partway through checking. */
734 return -1;
735 case 3: /* authentication process failed. */
736 return -2;
737 }
738 return -2;
739}
740
741/* add the [proxy]authorisation header */
c78aa667 742static void
2d70df72 743authDigestAddHeader(auth_user_request_t * auth_user_request, HttpReply * rep, int accel)
744{
e6ccf245 745 enum http_hdr_type type;
2d70df72 746 digest_request_h *digest_request;
747 if (!auth_user_request)
748 return;
2f44bd34 749 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
2d70df72 750 /* don't add to authentication error pages */
751 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
752 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
753 return;
754 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
755
756#if WAITING_FOR_TE
757 /* test for http/1.1 transfer chunked encoding */
758 if (chunkedtest)
759 return;
760#endif
761
762 if ((digestConfig->authenticate) && authDigestNonceLastRequest(digest_request->nonce)) {
763 digest_request->flags.authinfo_sent = 1;
764 debug(29, 9) ("authDigestAddHead: Sending type:%d header: 'nextnonce=\"%s\"", type, authenticateDigestNonceNonceb64(digest_request->nonce));
765 httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(digest_request->nonce));
766 }
767}
768
769#if WAITING_FOR_TE
770/* add the [proxy]authorisation header */
c78aa667 771static void
2d70df72 772authDigestAddTrailer(auth_user_request_t * auth_user_request, HttpReply * rep, int accel)
773{
774 int type;
775 digest_request_h *digest_request;
776 if (!auth_user_request)
777 return;
2f44bd34 778 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
2d70df72 779 /* has the header already been send? */
780 if (digest_request->flags.authinfo_sent)
781 return;
782 /* don't add to authentication error pages */
783 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
784 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
785 return;
786 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
787
788 if ((digestConfig->authenticate) && authDigestNonceLastRequest(digest_request->nonce)) {
789 debug(29, 9) ("authDigestAddTrailer: Sending type:%d header: 'nextnonce=\"%s\"", type, authenticateDigestNonceNonceb64(digest_request->nonce));
790 httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(digest_request->nonce));
791 }
792}
793#endif
794
795/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
796void
797authenticateDigestFixHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
798{
799 digest_request_h *digest_request;
800 int stale = 0;
801 digest_nonce_h *nonce = authenticateDigestNonceNew();
802 if (auth_user_request && authDigestAuthenticated(auth_user_request) && auth_user_request->scheme_data) {
2f44bd34 803 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
2d70df72 804 stale = authDigestNonceIsStale(digest_request->nonce);
805 }
806 if (digestConfig->authenticate) {
807 debug(29, 9) ("authenticateFixHeader: Sending type:%d header: 'Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s\n", type, digestConfig->digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false");
808 /* in the future, for WWW auth we may want to support the domain entry */
809 httpHeaderPutStrf(&rep->header, type, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s", digestConfig->digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false");
810 }
811}
812
c78aa667 813static void
2d70df72 814authenticateDigestUserFree(auth_user_t * auth_user)
815{
2f44bd34 816 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 817 dlink_node *link, *tmplink;
818 debug(29, 9) ("authenticateDigestFreeUser: Clearing Digest scheme data\n");
819 if (!digest_user)
820 return;
821 safe_free(digest_user->username);
822
823 link = digest_user->nonces.head;
824 while (link) {
825 tmplink = link;
826 link = link->next;
827 dlinkDelete(tmplink, &digest_user->nonces);
2f44bd34 828 authDigestNoncePurge(static_cast < digest_nonce_h * >(tmplink->data));
829 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
2d70df72 830 dlinkNodeDelete(tmplink);
831 }
832
833 memPoolFree(digest_user_pool, auth_user->scheme_data);
834 auth_user->scheme_data = NULL;
835}
836
837static void
838authenticateDigestHandleReply(void *data, char *reply)
839{
2f44bd34 840 DigestAuthenticateStateData *r = static_cast < DigestAuthenticateStateData * >(data);
2d70df72 841 auth_user_request_t *auth_user_request;
842 digest_request_h *digest_request;
843 digest_user_h *digest_user;
2d70df72 844 char *t = NULL;
fa80a8ef 845 void *cbdata;
2d70df72 846 debug(29, 9) ("authenticateDigestHandleReply: {%s}\n", reply ? reply : "<NULL>");
847 if (reply) {
848 if ((t = strchr(reply, ' ')))
849 *t = '\0';
850 if (*reply == '\0')
851 reply = NULL;
852 }
853 assert(r->auth_user_request != NULL);
854 auth_user_request = r->auth_user_request;
855 assert(auth_user_request->scheme_data != NULL);
2f44bd34 856 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
857 digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
2d70df72 858 if (reply && (strncasecmp(reply, "ERR", 3) == 0))
bd507204 859 digest_user->flags.credentials_ok = 3;
2d70df72 860 else {
861 CvtBin(reply, digest_user->HA1);
862 digest_user->HA1created = 1;
863 }
fa80a8ef 864 if (cbdataReferenceValidDone(r->data, &cbdata))
865 r->handler(cbdata, NULL);
2d70df72 866 authenticateStateFree(r);
867}
868
869/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
870 * config file */
871static void
872authDigestInit(authScheme * scheme)
873{
874 static int init = 0;
875 if (digestConfig->authenticate) {
876 authDigestUserSetup();
877 authDigestRequestSetup();
878 authenticateDigestNonceSetup();
879 authdigest_initialised = 1;
880 if (digestauthenticators == NULL)
881 digestauthenticators = helperCreate("digestauthenticator");
882 digestauthenticators->cmdline = digestConfig->authenticate;
883 digestauthenticators->n_to_start = digestConfig->authenticateChildren;
1ccc0d40 884 digestauthenticators->ipc_type = IPC_STREAM;
2d70df72 885 helperOpenServers(digestauthenticators);
886 if (!init) {
43717a1a 887 cachemgrRegister("digestauthenticator", "Digest User Authenticator Stats",
2d70df72 888 authenticateDigestStats, 0, 1);
889 init++;
890 }
e6ccf245 891 CBDATA_INIT_TYPE(DigestAuthenticateStateData);
2d70df72 892 }
893}
894
895
896/* free any allocated configuration details */
897void
898authDigestFreeConfig(authScheme * scheme)
899{
900 if (digestConfig == NULL)
901 return;
902 assert(digestConfig == scheme->scheme_data);
903 if (digestConfig->authenticate)
904 wordlistDestroy(&digestConfig->authenticate);
e6ccf245 905 safe_free(digestConfig->digestAuthRealm);
2d70df72 906 xfree(digestConfig);
907 digestConfig = NULL;
908}
909
910static void
911authDigestParse(authScheme * scheme, int n_configured, char *param_str)
912{
913 if (scheme->scheme_data == NULL) {
914 assert(digestConfig == NULL);
915 /* this is the first param to be found */
916 scheme->scheme_data = xmalloc(sizeof(auth_digest_config));
917 memset(scheme->scheme_data, 0, sizeof(auth_digest_config));
2f44bd34 918 digestConfig = static_cast < auth_digest_config * >(scheme->scheme_data);
2d70df72 919 digestConfig->authenticateChildren = 5;
920 /* 5 minutes */
921 digestConfig->nonceGCInterval = 5 * 60;
922 /* 30 minutes */
923 digestConfig->noncemaxduration = 30 * 60;
924 /* 50 requests */
925 digestConfig->noncemaxuses = 50;
d205783b 926 /* strict nonce count behaviour */
927 digestConfig->NonceStrictness = 1;
2d70df72 928 }
2f44bd34 929 digestConfig = static_cast < auth_digest_config * >(scheme->scheme_data);
2d70df72 930 if (strcasecmp(param_str, "program") == 0) {
931 if (digestConfig->authenticate)
932 wordlistDestroy(&digestConfig->authenticate);
933 parse_wordlist(&digestConfig->authenticate);
934 requirePathnameExists("authparam digest program", digestConfig->authenticate->key);
935 } else if (strcasecmp(param_str, "children") == 0) {
936 parse_int(&digestConfig->authenticateChildren);
937 } else if (strcasecmp(param_str, "realm") == 0) {
938 parse_eol(&digestConfig->digestAuthRealm);
939 } else if (strcasecmp(param_str, "nonce_garbage_interval") == 0) {
940 parse_time_t(&digestConfig->nonceGCInterval);
941 } else if (strcasecmp(param_str, "nonce_max_duration") == 0) {
942 parse_time_t(&digestConfig->noncemaxduration);
943 } else if (strcasecmp(param_str, "nonce_max_count") == 0) {
2f44bd34 944 parse_int((int *) &digestConfig->noncemaxuses);
d205783b 945 } else if (strcasecmp(param_str, "nonce_strictness") == 0) {
2f44bd34 946 parse_onoff(&digestConfig->NonceStrictness);
2d70df72 947 } else {
948 debug(28, 0) ("unrecognised digest auth scheme parameter '%s'\n", param_str);
949 }
950}
951
952
953static void
954authenticateDigestStats(StoreEntry * sentry)
955{
956 storeAppendPrintf(sentry, "Digest Authenticator Statistics:\n");
957 helperStats(sentry, digestauthenticators);
958}
959
960/* NonceUserUnlink: remove the reference to auth_user and unlink the node from the list */
961
c78aa667 962static void
2d70df72 963authDigestNonceUserUnlink(digest_nonce_h * nonce)
964{
965 digest_user_h *digest_user;
966 dlink_node *link, *tmplink;
967 if (!nonce)
968 return;
969 if (!nonce->auth_user)
970 return;
2f44bd34 971 digest_user = static_cast < digest_user_h * >(nonce->auth_user->scheme_data);
2d70df72 972 /* unlink from the user list. Yes we're crossing structures but this is the only
973 * time this code is needed
974 */
975 link = digest_user->nonces.head;
976 while (link) {
977 tmplink = link;
978 link = link->next;
979 if (tmplink->data == nonce) {
980 dlinkDelete(tmplink, &digest_user->nonces);
2f44bd34 981 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
2d70df72 982 dlinkNodeDelete(tmplink);
983 link = NULL;
984 }
985 }
986 /* this reference to auth_user was not locked because freeeing the auth_user frees
987 * the nonce too.
988 */
989 nonce->auth_user = NULL;
990}
991
992/* authDigestUserLinkNonce: add a nonce to a given user's struct */
993
c78aa667 994static void
2d70df72 995authDigestUserLinkNonce(auth_user_t * auth_user, digest_nonce_h * nonce)
996{
997 dlink_node *node;
998 digest_user_h *digest_user;
999 if (!auth_user || !nonce)
1000 return;
1001 if (!auth_user->scheme_data)
1002 return;
2f44bd34 1003 digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 1004 node = digest_user->nonces.head;
1005 while (node && (node->data != nonce))
1006 node = node->next;
1007 if (node)
1008 return;
1009 node = dlinkNodeNew();
1010 dlinkAddTail(nonce, node, &digest_user->nonces);
1011 authDigestNonceLink(nonce);
1012 /* ping this nonce to this auth user */
1013 assert((nonce->auth_user == NULL) || (nonce->auth_user = auth_user));
1014 /* we don't lock this reference because removing the auth_user removes the
1015 * hash too. Of course if that changes we're stuffed so read the code huh?
1016 */
1017 nonce->auth_user = auth_user;
1018}
1019
1020/* authenticateDigestUsername: return a pointer to the username in the */
e6ccf245 1021static char const *
2f44bd34 1022authenticateDigestUsername(auth_user_t const *auth_user)
2d70df72 1023{
2f44bd34 1024 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 1025 if (digest_user)
1026 return digest_user->username;
1027 return NULL;
1028}
1029
1030/* setup the necessary info to log the username */
c78aa667 1031static void
2d70df72 1032authDigestLogUsername(auth_user_request_t * auth_user_request, char *username)
1033{
1034 auth_user_t *auth_user;
1035 digest_user_h *digest_user;
1036 dlink_node *node;
1037
1038 /* log the username */
1039 debug(29, 9) ("authBasicDecodeAuth: Creating new user for logging '%s'\n", username);
1040 /* new auth_user */
1041 auth_user = authenticateAuthUserNew("digest");
1042 /* new scheme data */
1043 digest_user = authDigestUserNew();
1044 /* save the credentials */
1045 digest_user->username = username;
1046 /* link the scheme data in */
1047 auth_user->scheme_data = digest_user;
1048 /* set the auth_user type */
1049 auth_user->auth_type = AUTH_BROKEN;
1050 /* link the request to the user */
1051 auth_user_request->auth_user = auth_user;
1052 /* lock for the auth_user_request link */
1053 authenticateAuthUserLock(auth_user);
1054 node = dlinkNodeNew();
1055 dlinkAdd(auth_user_request, node, &auth_user->requests);
1056}
1057
1058/*
1059 * Decode a Digest [Proxy-]Auth string, placing the results in the passed
1060 * Auth_user structure.
1061 */
1062
1063static void
1064authenticateDigestDecodeAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
1065{
2d70df72 1066 const char *item;
1067 const char *p;
1068 const char *pos = NULL;
1069 char *username = NULL;
1070 digest_nonce_h *nonce;
1071 int ilen;
1072 digest_request_h *digest_request;
1073 digest_user_h *digest_user;
1074 auth_user_t *auth_user;
1075 dlink_node *node;
1076
1077 debug(29, 9) ("authenticateDigestDecodeAuth: beginning\n");
1078 assert(auth_user_request != NULL);
1079
1080 digest_request = authDigestRequestNew();
1081
1082 /* trim DIGEST from string */
1083 while (!xisspace(*proxy_auth))
1084 proxy_auth++;
1085
1086 /* Trim leading whitespace before decoding */
1087 while (xisspace(*proxy_auth))
1088 proxy_auth++;
1089
2f44bd34 1090 String temp(proxy_auth);
2d70df72 1091 while (strListGetItem(&temp, ',', &item, &ilen, &pos)) {
1092 if ((p = strchr(item, '=')) && (p - item < ilen))
1093 ilen = p++ - item;
1094 if (!strncmp(item, "username", ilen)) {
1095 /* white space */
1096 while (xisspace(*p))
1097 p++;
1098 /* quote mark */
1099 p++;
1100 username = xstrndup(p, strchr(p, '"') + 1 - p);
1101 debug(29, 9) ("authDigestDecodeAuth: Found Username '%s'\n", username);
1102 } else if (!strncmp(item, "realm", ilen)) {
1103 /* white space */
1104 while (xisspace(*p))
1105 p++;
1106 /* quote mark */
1107 p++;
1108 digest_request->realm = xstrndup(p, strchr(p, '"') + 1 - p);
1109 debug(29, 9) ("authDigestDecodeAuth: Found realm '%s'\n", digest_request->realm);
1110 } else if (!strncmp(item, "qop", ilen)) {
1111 /* white space */
1112 while (xisspace(*p))
1113 p++;
d205783b 1114 if (*p == '\"')
2f44bd34 1115 /* quote mark */
1116 p++;
d205783b 1117 digest_request->qop = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
2d70df72 1118 debug(29, 9) ("authDigestDecodeAuth: Found qop '%s'\n", digest_request->qop);
1119 } else if (!strncmp(item, "algorithm", ilen)) {
1120 /* white space */
1121 while (xisspace(*p))
1122 p++;
d205783b 1123 if (*p == '\"')
2f44bd34 1124 /* quote mark */
1125 p++;
1126 digest_request->algorithm = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
2d70df72 1127 debug(29, 9) ("authDigestDecodeAuth: Found algorithm '%s'\n", digest_request->algorithm);
1128 } else if (!strncmp(item, "uri", ilen)) {
1129 /* white space */
1130 while (xisspace(*p))
1131 p++;
1132 /* quote mark */
1133 p++;
1134 digest_request->uri = xstrndup(p, strchr(p, '"') + 1 - p);
1135 debug(29, 9) ("authDigestDecodeAuth: Found uri '%s'\n", digest_request->uri);
1136 } else if (!strncmp(item, "nonce", ilen)) {
1137 /* white space */
1138 while (xisspace(*p))
1139 p++;
1140 /* quote mark */
1141 p++;
1142 digest_request->nonceb64 = xstrndup(p, strchr(p, '"') + 1 - p);
1143 debug(29, 9) ("authDigestDecodeAuth: Found nonce '%s'\n", digest_request->nonceb64);
1144 } else if (!strncmp(item, "nc", ilen)) {
1145 /* white space */
1146 while (xisspace(*p))
1147 p++;
1148 xstrncpy(digest_request->nc, p, 9);
1149 debug(29, 9) ("authDigestDecodeAuth: Found noncecount '%s'\n", digest_request->nc);
1150 } else if (!strncmp(item, "cnonce", ilen)) {
1151 /* white space */
1152 while (xisspace(*p))
1153 p++;
1154 /* quote mark */
1155 p++;
1156 digest_request->cnonce = xstrndup(p, strchr(p, '"') + 1 - p);
1157 debug(29, 9) ("authDigestDecodeAuth: Found cnonce '%s'\n", digest_request->cnonce);
1158 } else if (!strncmp(item, "response", ilen)) {
1159 /* white space */
1160 while (xisspace(*p))
1161 p++;
1162 /* quote mark */
1163 p++;
1164 digest_request->response = xstrndup(p, strchr(p, '"') + 1 - p);
1165 debug(29, 9) ("authDigestDecodeAuth: Found response '%s'\n", digest_request->response);
1166 }
1167 }
528b2c61 1168 temp.clean();
2d70df72 1169
1170
1171 /* now we validate the data given to us */
1172
74830fc8 1173 /*
1174 * TODO: on invalid parameters we should return 400, not 407.
1175 * Find some clean way of doing this. perhaps return a valid
1176 * struct, and set the direction to clientwards combined with
1177 * a change to the clientwards handling code (ie let the
1178 * clientwards call set the error type (but limited to known
1179 * correct values - 400/401/407
1180 */
2d70df72 1181
1182 /* first the NONCE count */
1183 if (digest_request->cnonce && strlen(digest_request->nc) != 8) {
1184 debug(29, 4) ("authenticateDigestDecode: nonce count length invalid\n");
1185 authDigestLogUsername(auth_user_request, username);
1186
1187 /* we don't need the scheme specific data anymore */
1188 authDigestRequestDelete(digest_request);
1189 auth_user_request->scheme_data = NULL;
1190 return;
1191 }
1192 /* now the nonce */
1193 nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64);
1194 if ((nonce == NULL) || !(authDigestNonceIsValid(nonce, digest_request->nc))) {
1195 /* we couldn't find a matching nonce! */
1196 debug(29, 4) ("authenticateDigestDecode: Unexpected or invalid nonce recieved\n");
1197 authDigestLogUsername(auth_user_request, username);
1198
1199 /* we don't need the scheme specific data anymore */
1200 authDigestRequestDelete(digest_request);
1201 auth_user_request->scheme_data = NULL;
1202 return;
1203 }
1204 digest_request->nonce = nonce;
2d70df72 1205 authDigestNonceLink(nonce);
1206
d205783b 1207 /* check the qop is what we expected. Note that for compatability with
1208 * RFC 2069 we should support a missing qop. Tough. */
1209 if (!digest_request->qop || strcmp(digest_request->qop, QOP_AUTH)) {
2d70df72 1210 /* we recieved a qop option we didn't send */
1211 debug(29, 4) ("authenticateDigestDecode: Invalid qop option recieved\n");
1212 authDigestLogUsername(auth_user_request, username);
1213
1214 /* we don't need the scheme specific data anymore */
1215 authDigestRequestDelete(digest_request);
1216 auth_user_request->scheme_data = NULL;
1217 return;
1218 }
74830fc8 1219 /* we can't check the URI just yet. We'll check it in the
9bea1d5b 1220 * authenticate phase */
2d70df72 1221
1222 /* is the response the correct length? */
1223
1224 if (!digest_request->response || strlen(digest_request->response) != 32) {
1225 debug(29, 4) ("authenticateDigestDecode: Response length invalid\n");
1226 authDigestLogUsername(auth_user_request, username);
1227
1228 /* we don't need the scheme specific data anymore */
1229 authDigestRequestDelete(digest_request);
1230 auth_user_request->scheme_data = NULL;
1231 return;
1232 }
1233 /* do we have a username ? */
1234 if (!username || username[0] == '\0') {
1235 debug(29, 4) ("authenticateDigestDecode: Empty or not present username\n");
1236 authDigestLogUsername(auth_user_request, username);
1237
1238 /* we don't need the scheme specific data anymore */
1239 authDigestRequestDelete(digest_request);
1240 auth_user_request->scheme_data = NULL;
1241 return;
1242 }
1243 /* check that we're not being hacked / the username hasn't changed */
e6ccf245 1244 if (nonce->auth_user && strcmp(username, nonce->auth_user->username())) {
2d70df72 1245 debug(29, 4) ("authenticateDigestDecode: Username for the nonce does not equal the username for the request\n");
1246 authDigestLogUsername(auth_user_request, username);
1247
1248 /* we don't need the scheme specific data anymore */
1249 authDigestRequestDelete(digest_request);
1250 auth_user_request->scheme_data = NULL;
1251 return;
1252 }
1253 /* if we got a qop, did we get a cnonce or did we get a cnonce wihtout a qop? */
1254 if ((digest_request->qop && !digest_request->cnonce)
1255 || (!digest_request->qop && digest_request->cnonce)) {
1256 debug(29, 4) ("authenticateDigestDecode: qop without cnonce, or vice versa!\n");
1257 authDigestLogUsername(auth_user_request, username);
1258
1259 /* we don't need the scheme specific data anymore */
1260 authDigestRequestDelete(digest_request);
1261 auth_user_request->scheme_data = NULL;
1262 return;
1263 }
1264 /* check the algorithm is present and supported */
d205783b 1265 if (!digest_request->algorithm)
2f44bd34 1266 digest_request->algorithm = xstrndup("MD5", 4);
d205783b 1267 else if (strcmp(digest_request->algorithm, "MD5")
2d70df72 1268 && strcmp(digest_request->algorithm, "MD5-sess")) {
1269 debug(29, 4) ("authenticateDigestDecode: invalid algorithm specified!\n");
1270 authDigestLogUsername(auth_user_request, username);
1271
1272 /* we don't need the scheme specific data anymore */
1273 authDigestRequestDelete(digest_request);
1274 auth_user_request->scheme_data = NULL;
1275 return;
1276 }
1277 /* the method we'll check at the authenticate step as well */
1278
1279
1280 /* we don't send or parse opaques. Ok so we're flexable ... */
1281
1282 /* find the user */
1283
1284 if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
1285 /* the user doesn't exist in the username cache yet */
1286 debug(29, 9) ("authDigestDecodeAuth: Creating new digest user '%s'\n", username);
1287 /* new auth_user */
1288 auth_user = authenticateAuthUserNew("digest");
1289 /* new scheme user data */
1290 digest_user = authDigestUserNew();
1291 /* save the username */
1292 digest_user->username = username;
1293 /* link the primary struct in */
1294 auth_user->scheme_data = digest_user;
1295 /* set the user type */
1296 auth_user->auth_type = AUTH_DIGEST;
74830fc8 1297 /* this auth_user struct is the one to get added to the
9bea1d5b 1298 * username cache */
2d70df72 1299 /* store user in hash's */
1300 authenticateUserNameCacheAdd(auth_user);
1301 /*
74830fc8 1302 * Add the digest to the user so we can tell if a hacking
1303 * or spoofing attack is taking place. We do this by assuming
1304 * the user agent won't change user name without warning.
2d70df72 1305 */
1306 authDigestUserLinkNonce(auth_user, nonce);
1307 } else {
33272404 1308 debug(29, 9) ("authDigestDecodeAuth: Found user '%s' in the user cache as '%p'\n", username, auth_user);
2f44bd34 1309 digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 1310 xfree(username);
1311 }
1312 /*link the request and the user */
1313 auth_user_request->auth_user = auth_user;
1314 auth_user_request->scheme_data = digest_request;
1315 /* lock for the request link */
1316 authenticateAuthUserLock(auth_user);
1317 node = dlinkNodeNew();
1318 dlinkAdd(auth_user_request, node, &auth_user->requests);
1319
33272404 1320 debug(29, 9) ("username = '%s'\nrealm = '%s'\nqop = '%s'\nalgorithm = '%s'\nuri = '%s'\nnonce = '%s'\nnc = '%s'\ncnonce = '%s'\nresponse = '%s'\ndigestnonce = '%p'\n",
2d70df72 1321 digest_user->username, digest_request->realm,
1322 digest_request->qop, digest_request->algorithm,
1323 digest_request->uri, digest_request->nonceb64,
1324 digest_request->nc, digest_request->cnonce, digest_request->response, nonce);
1325
1326 return;
1327}
1328
1329/* send the initial data to a digest authenticator module */
1330static void
1331authenticateDigestStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
1332{
e6ccf245 1333 DigestAuthenticateStateData *r = NULL;
2d70df72 1334 char buf[8192];
1335 digest_request_h *digest_request;
1336 digest_user_h *digest_user;
1337 assert(auth_user_request);
1338 assert(handler);
1339 assert(auth_user_request->auth_user->auth_type == AUTH_DIGEST);
1340 assert(auth_user_request->auth_user->scheme_data != NULL);
1341 assert(auth_user_request->scheme_data != NULL);
2f44bd34 1342 digest_request = static_cast < digest_request_h * >(auth_user_request->scheme_data);
1343 digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
2d70df72 1344 debug(29, 9) ("authenticateStart: '\"%s\":\"%s\"'\n", digest_user->username,
1345 digest_request->realm);
1346 if (digestConfig->authenticate == NULL) {
1347 handler(data, NULL);
1348 return;
1349 }
e6ccf245 1350 r = cbdataAlloc(DigestAuthenticateStateData);
2d70df72 1351 r->handler = handler;
fa80a8ef 1352 r->data = cbdataReference(data);
2d70df72 1353 r->auth_user_request = auth_user_request;
1354 snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username, digest_request->realm);
1355 helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r);
1356}