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