]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/digest/auth_digest.cc
Summary: Final MSVC fixups.
[thirdparty/squid.git] / src / auth / digest / auth_digest.cc
CommitLineData
2d70df72 1
2/*
190154cf 3 * $Id: auth_digest.cc,v 1.31 2003/08/10 11:00:48 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"
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
696 if (strcasecmp(digest_request->response, Response)) {
f5292c64 697 if (digestConfig->PostWorkaround && request->method != METHOD_GET) {
698 /* Ugly workaround for certain very broken browsers using the
699 * wrong method to calculate the request-digest on POST request.
700 * This should be deleted once Digest authentication becomes more
701 * widespread and such broken browsers no longer are commonly
702 * used.
703 */
704 DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
705 digest_request->nc, digest_request->cnonce, digest_request->qop,
706 RequestMethodStr[METHOD_GET], digest_request->uri, HA2, Response);
707
708 if (strcasecmp(digest_request->response, Response)) {
709 credentials(Failed);
710 return;
711 } else {
712 const char *useragent = httpHeaderGetStr(&request->header, HDR_USER_AGENT);
713
c3edaed6 714 static struct in_addr last_broken_addr;
715 static int seen_broken_client = 0;
716
717 if (!seen_broken_client) {
718 last_broken_addr = no_addr;
719 seen_broken_client = 1;
720 }
f5292c64 721
722 if (memcmp(&last_broken_addr, &request->client_addr, sizeof(last_broken_addr)) != 0) {
723 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 : "-");
724 last_broken_addr = request->client_addr;
725 }
726 }
727 } else {
728 credentials(Failed);
729 return;
730 }
731
732 /* check for stale nonce */
733 if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
734 debug(29, 3) ("authenticateDigestAuthenticateuser: user '%s' validated OK but nonce stale\n",
735 digest_user->username);
736 digest_request->flags.nonce_stale = 1;
737 credentials(Failed);
738 return;
739 }
2d70df72 740 }
62e76326 741
82b045dc 742 credentials(Ok);
f5292c64 743
2d70df72 744 /* password was checked and did match */
745 debug(29, 4) ("authenticateDigestAuthenticateuser: user '%s' validated OK\n",
62e76326 746 digest_user->username);
2d70df72 747
748 /* auth_user is now linked, we reset these values
749 * after external auth occurs anyway */
750 auth_user->expiretime = current_time.tv_sec;
2d70df72 751 return;
752}
753
82b045dc 754int
755digest_request_h::direction()
2d70df72 756{
82b045dc 757 switch (credentials()) {
62e76326 758
82b045dc 759 case Unchecked:
62e76326 760 return -1;
761
82b045dc 762 case Ok:
62e76326 763
62e76326 764 return 0;
765
82b045dc 766 case Pending:
62e76326 767 return -1;
768
82b045dc 769 case Failed:
f5292c64 770
88d7e7f4 771 if (flags.nonce_stale)
f5292c64 772 /* nonce is stale, send new challenge */
773 return 1;
774
62e76326 775 return -2;
2d70df72 776 }
62e76326 777
2d70df72 778 return -2;
779}
780
781/* add the [proxy]authorisation header */
82b045dc 782void
783digest_request_h::addHeader(HttpReply * rep, int accel)
2d70df72 784{
6e3d4bbc 785 http_hdr_type type;
62e76326 786
2d70df72 787 /* don't add to authentication error pages */
82b045dc 788
2d70df72 789 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
62e76326 790 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
791 return;
792
2d70df72 793 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
794
795#if WAITING_FOR_TE
796 /* test for http/1.1 transfer chunked encoding */
797 if (chunkedtest)
62e76326 798 return;
799
2d70df72 800#endif
801
82b045dc 802 if ((digestConfig->authenticate) && authDigestNonceLastRequest(nonce)) {
803 flags.authinfo_sent = 1;
804 debug(29, 9) ("authDigestAddHead: Sending type:%d header: 'nextnonce=\"%s\"", type, authenticateDigestNonceNonceb64(nonce));
805 httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
2d70df72 806 }
807}
808
809#if WAITING_FOR_TE
810/* add the [proxy]authorisation header */
c78aa667 811static void
2d70df72 812authDigestAddTrailer(auth_user_request_t * auth_user_request, HttpReply * rep, int accel)
813{
814 int type;
815 digest_request_h *digest_request;
62e76326 816
2d70df72 817 if (!auth_user_request)
62e76326 818 return;
819
82b045dc 820 digest_request = dynamic_cast < digest_request_h * >(auth_user_request->state());
62e76326 821
2d70df72 822 /* has the header already been send? */
823 if (digest_request->flags.authinfo_sent)
62e76326 824 return;
825
2d70df72 826 /* don't add to authentication error pages */
827 if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
62e76326 828 || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
829 return;
830
2d70df72 831 type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
832
833 if ((digestConfig->authenticate) && authDigestNonceLastRequest(digest_request->nonce)) {
62e76326 834 debug(29, 9) ("authDigestAddTrailer: Sending type:%d header: 'nextnonce=\"%s\"", type, authenticateDigestNonceNonceb64(digest_request->nonce));
835 httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(digest_request->nonce));
2d70df72 836 }
837}
62e76326 838
2d70df72 839#endif
840
841/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
842void
190154cf 843authenticateDigestFixHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, HttpRequest * request)
2d70df72 844{
82b045dc 845 if (!digestConfig->authenticate)
846 return;
847
2d70df72 848 int stale = 0;
62e76326 849
82b045dc 850 if (auth_user_request && auth_user_request->state()) {
851 digest_request_h *digest_request;
852 digest_request = dynamic_cast < digest_request_h * >(auth_user_request->state());
853 assert (digest_request);
62e76326 854
f5292c64 855 stale = digest_request->flags.nonce_stale;
2d70df72 856 }
82b045dc 857
858 /* on a 407 or 401 we always use a new nonce */
859 digest_nonce_h *nonce = authenticateDigestNonceNew();
860
861 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");
862
863 /* in the future, for WWW auth we may want to support the domain entry */
864 httpHeaderPutStrf(&rep->header, type, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s", digestConfig->digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false");
2d70df72 865}
866
c78aa667 867static void
2d70df72 868authenticateDigestUserFree(auth_user_t * auth_user)
869{
2f44bd34 870 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
2d70df72 871 debug(29, 9) ("authenticateDigestFreeUser: Clearing Digest scheme data\n");
62e76326 872
2d70df72 873 if (!digest_user)
62e76326 874 return;
875
82b045dc 876 delete digest_user;
2d70df72 877
82b045dc 878 auth_user->scheme_data = NULL;
879}
880
881digest_user_h::~digest_user_h()
882{
883 safe_free(username);
884
885 dlink_node *link, *tmplink;
886 link = nonces.head;
62e76326 887
2d70df72 888 while (link) {
62e76326 889 tmplink = link;
890 link = link->next;
82b045dc 891 dlinkDelete(tmplink, &nonces);
62e76326 892 authDigestNoncePurge(static_cast < digest_nonce_h * >(tmplink->data));
893 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
894 dlinkNodeDelete(tmplink);
2d70df72 895 }
2d70df72 896}
897
898static void
899authenticateDigestHandleReply(void *data, char *reply)
900{
82b045dc 901 DigestAuthenticateStateData *replyData = static_cast < DigestAuthenticateStateData * >(data);
2d70df72 902 auth_user_request_t *auth_user_request;
903 digest_request_h *digest_request;
904 digest_user_h *digest_user;
2d70df72 905 char *t = NULL;
fa80a8ef 906 void *cbdata;
2d70df72 907 debug(29, 9) ("authenticateDigestHandleReply: {%s}\n", reply ? reply : "<NULL>");
62e76326 908
2d70df72 909 if (reply) {
62e76326 910 if ((t = strchr(reply, ' ')))
911 *t = '\0';
912
913 if (*reply == '\0')
914 reply = NULL;
2d70df72 915 }
62e76326 916
82b045dc 917 assert(replyData->auth_user_request != NULL);
918 auth_user_request = replyData->auth_user_request;
919 assert(auth_user_request->state() != NULL);
920 digest_request = dynamic_cast < digest_request_h * >(auth_user_request->state());
2f44bd34 921 digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
62e76326 922
2d70df72 923 if (reply && (strncasecmp(reply, "ERR", 3) == 0))
82b045dc 924 digest_request->credentials(digest_request_h::Failed);
2d70df72 925 else {
62e76326 926 CvtBin(reply, digest_user->HA1);
927 digest_user->HA1created = 1;
2d70df72 928 }
62e76326 929
82b045dc 930 if (cbdataReferenceValidDone(replyData->data, &cbdata))
931 replyData->handler(cbdata, NULL);
62e76326 932
82b045dc 933 cbdataFree(replyData);
2d70df72 934}
935
936/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
937 * config file */
938static void
939authDigestInit(authScheme * scheme)
940{
941 static int init = 0;
62e76326 942
2d70df72 943 if (digestConfig->authenticate) {
62e76326 944 authenticateDigestNonceSetup();
945 authdigest_initialised = 1;
946
947 if (digestauthenticators == NULL)
948 digestauthenticators = helperCreate("digestauthenticator");
949
950 digestauthenticators->cmdline = digestConfig->authenticate;
951
952 digestauthenticators->n_to_start = digestConfig->authenticateChildren;
953
954 digestauthenticators->ipc_type = IPC_STREAM;
955
956 helperOpenServers(digestauthenticators);
957
958 if (!init) {
959 cachemgrRegister("digestauthenticator", "Digest User Authenticator Stats",
960 authenticateDigestStats, 0, 1);
961 init++;
962 }
963
964 CBDATA_INIT_TYPE(DigestAuthenticateStateData);
2d70df72 965 }
966}
967
968
969/* free any allocated configuration details */
970void
971authDigestFreeConfig(authScheme * scheme)
972{
973 if (digestConfig == NULL)
62e76326 974 return;
975
2d70df72 976 assert(digestConfig == scheme->scheme_data);
62e76326 977
2d70df72 978 if (digestConfig->authenticate)
62e76326 979 wordlistDestroy(&digestConfig->authenticate);
980
e6ccf245 981 safe_free(digestConfig->digestAuthRealm);
62e76326 982
2d70df72 983 xfree(digestConfig);
62e76326 984
2d70df72 985 digestConfig = NULL;
986}
987
988static void
989authDigestParse(authScheme * scheme, int n_configured, char *param_str)
990{
991 if (scheme->scheme_data == NULL) {
62e76326 992 assert(digestConfig == NULL);
993 /* this is the first param to be found */
994 scheme->scheme_data = xmalloc(sizeof(auth_digest_config));
995 memset(scheme->scheme_data, 0, sizeof(auth_digest_config));
996 digestConfig = static_cast < auth_digest_config * >(scheme->scheme_data);
997 digestConfig->authenticateChildren = 5;
998 /* 5 minutes */
999 digestConfig->nonceGCInterval = 5 * 60;
1000 /* 30 minutes */
1001 digestConfig->noncemaxduration = 30 * 60;
1002 /* 50 requests */
1003 digestConfig->noncemaxuses = 50;
f5292c64 1004 /* Not strict nonce count behaviour */
1005 digestConfig->NonceStrictness = 0;
1006 /* Verify nonce count */
1007 digestConfig->CheckNonceCount = 1;
2d70df72 1008 }
62e76326 1009
2f44bd34 1010 digestConfig = static_cast < auth_digest_config * >(scheme->scheme_data);
62e76326 1011
2d70df72 1012 if (strcasecmp(param_str, "program") == 0) {
62e76326 1013 if (digestConfig->authenticate)
1014 wordlistDestroy(&digestConfig->authenticate);
1015
1016 parse_wordlist(&digestConfig->authenticate);
1017
1018 requirePathnameExists("authparam digest program", digestConfig->authenticate->key);
2d70df72 1019 } else if (strcasecmp(param_str, "children") == 0) {
62e76326 1020 parse_int(&digestConfig->authenticateChildren);
2d70df72 1021 } else if (strcasecmp(param_str, "realm") == 0) {
62e76326 1022 parse_eol(&digestConfig->digestAuthRealm);
2d70df72 1023 } else if (strcasecmp(param_str, "nonce_garbage_interval") == 0) {
62e76326 1024 parse_time_t(&digestConfig->nonceGCInterval);
2d70df72 1025 } else if (strcasecmp(param_str, "nonce_max_duration") == 0) {
62e76326 1026 parse_time_t(&digestConfig->noncemaxduration);
2d70df72 1027 } else if (strcasecmp(param_str, "nonce_max_count") == 0) {
62e76326 1028 parse_int((int *) &digestConfig->noncemaxuses);
d205783b 1029 } else if (strcasecmp(param_str, "nonce_strictness") == 0) {
62e76326 1030 parse_onoff(&digestConfig->NonceStrictness);
f5292c64 1031 } else if (strcasecmp(param_str, "check_nonce_count") == 0) {
1032 parse_onoff(&digestConfig->CheckNonceCount);
1033 } else if (strcasecmp(param_str, "post_workaround") == 0) {
1034 parse_onoff(&digestConfig->PostWorkaround);
2d70df72 1035 } else {
62e76326 1036 debug(28, 0) ("unrecognised digest auth scheme parameter '%s'\n", param_str);
2d70df72 1037 }
1038}
1039
1040
1041static void
1042authenticateDigestStats(StoreEntry * sentry)
1043{
1044 storeAppendPrintf(sentry, "Digest Authenticator Statistics:\n");
1045 helperStats(sentry, digestauthenticators);
1046}
1047
1048/* NonceUserUnlink: remove the reference to auth_user and unlink the node from the list */
1049
c78aa667 1050static void
2d70df72 1051authDigestNonceUserUnlink(digest_nonce_h * nonce)
1052{
1053 digest_user_h *digest_user;
1054 dlink_node *link, *tmplink;
62e76326 1055
2d70df72 1056 if (!nonce)
62e76326 1057 return;
1058
2d70df72 1059 if (!nonce->auth_user)
62e76326 1060 return;
1061
2f44bd34 1062 digest_user = static_cast < digest_user_h * >(nonce->auth_user->scheme_data);
62e76326 1063
1064 /* unlink from the user list. Yes we're crossing structures but this is the only
2d70df72 1065 * time this code is needed
1066 */
1067 link = digest_user->nonces.head;
62e76326 1068
2d70df72 1069 while (link) {
62e76326 1070 tmplink = link;
1071 link = link->next;
1072
1073 if (tmplink->data == nonce) {
1074 dlinkDelete(tmplink, &digest_user->nonces);
1075 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
1076 dlinkNodeDelete(tmplink);
1077 link = NULL;
1078 }
2d70df72 1079 }
62e76326 1080
2d70df72 1081 /* this reference to auth_user was not locked because freeeing the auth_user frees
1082 * the nonce too.
1083 */
1084 nonce->auth_user = NULL;
1085}
1086
1087/* authDigestUserLinkNonce: add a nonce to a given user's struct */
1088
c78aa667 1089static void
2d70df72 1090authDigestUserLinkNonce(auth_user_t * auth_user, digest_nonce_h * nonce)
1091{
1092 dlink_node *node;
1093 digest_user_h *digest_user;
62e76326 1094
2d70df72 1095 if (!auth_user || !nonce)
62e76326 1096 return;
1097
2d70df72 1098 if (!auth_user->scheme_data)
62e76326 1099 return;
1100
2f44bd34 1101 digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
62e76326 1102
2d70df72 1103 node = digest_user->nonces.head;
62e76326 1104
2d70df72 1105 while (node && (node->data != nonce))
62e76326 1106 node = node->next;
1107
2d70df72 1108 if (node)
62e76326 1109 return;
1110
2d70df72 1111 node = dlinkNodeNew();
62e76326 1112
2d70df72 1113 dlinkAddTail(nonce, node, &digest_user->nonces);
62e76326 1114
2d70df72 1115 authDigestNonceLink(nonce);
62e76326 1116
2d70df72 1117 /* ping this nonce to this auth user */
1118 assert((nonce->auth_user == NULL) || (nonce->auth_user = auth_user));
62e76326 1119
1120 /* we don't lock this reference because removing the auth_user removes the
2d70df72 1121 * hash too. Of course if that changes we're stuffed so read the code huh?
1122 */
1123 nonce->auth_user = auth_user;
1124}
1125
1126/* authenticateDigestUsername: return a pointer to the username in the */
e6ccf245 1127static char const *
2f44bd34 1128authenticateDigestUsername(auth_user_t const *auth_user)
2d70df72 1129{
2f44bd34 1130 digest_user_h *digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
62e76326 1131
2d70df72 1132 if (digest_user)
62e76326 1133 return digest_user->username;
1134
2d70df72 1135 return NULL;
1136}
1137
1138/* setup the necessary info to log the username */
c78aa667 1139static void
2d70df72 1140authDigestLogUsername(auth_user_request_t * auth_user_request, char *username)
1141{
1142 auth_user_t *auth_user;
1143 digest_user_h *digest_user;
1144 dlink_node *node;
1145
1146 /* log the username */
1147 debug(29, 9) ("authBasicDecodeAuth: Creating new user for logging '%s'\n", username);
1148 /* new auth_user */
1149 auth_user = authenticateAuthUserNew("digest");
1150 /* new scheme data */
82b045dc 1151 digest_user = new digest_user_h;
2d70df72 1152 /* save the credentials */
1153 digest_user->username = username;
1154 /* link the scheme data in */
1155 auth_user->scheme_data = digest_user;
1156 /* set the auth_user type */
1157 auth_user->auth_type = AUTH_BROKEN;
1158 /* link the request to the user */
1159 auth_user_request->auth_user = auth_user;
1160 /* lock for the auth_user_request link */
1161 authenticateAuthUserLock(auth_user);
1162 node = dlinkNodeNew();
1163 dlinkAdd(auth_user_request, node, &auth_user->requests);
1164}
1165
1166/*
1167 * Decode a Digest [Proxy-]Auth string, placing the results in the passed
1168 * Auth_user structure.
1169 */
1170
1171static void
1172authenticateDigestDecodeAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
1173{
2d70df72 1174 const char *item;
1175 const char *p;
1176 const char *pos = NULL;
1177 char *username = NULL;
1178 digest_nonce_h *nonce;
1179 int ilen;
1180 digest_request_h *digest_request;
1181 digest_user_h *digest_user;
1182 auth_user_t *auth_user;
1183 dlink_node *node;
1184
1185 debug(29, 9) ("authenticateDigestDecodeAuth: beginning\n");
1186 assert(auth_user_request != NULL);
82b045dc 1187 assert (auth_user_request->state() == NULL);
2d70df72 1188
82b045dc 1189 digest_request = new digest_request_h;
2d70df72 1190
1191 /* trim DIGEST from string */
62e76326 1192
2d70df72 1193 while (!xisspace(*proxy_auth))
62e76326 1194 proxy_auth++;
2d70df72 1195
1196 /* Trim leading whitespace before decoding */
1197 while (xisspace(*proxy_auth))
62e76326 1198 proxy_auth++;
2d70df72 1199
2f44bd34 1200 String temp(proxy_auth);
62e76326 1201
2d70df72 1202 while (strListGetItem(&temp, ',', &item, &ilen, &pos)) {
62e76326 1203 if ((p = strchr(item, '=')) && (p - item < ilen))
1204 ilen = p++ - item;
1205
1206 if (!strncmp(item, "username", ilen)) {
1207 /* white space */
1208
1209 while (xisspace(*p))
1210 p++;
1211
1212 /* quote mark */
1213 p++;
1214
1215 username = xstrndup(p, strchr(p, '"') + 1 - p);
1216
1217 debug(29, 9) ("authDigestDecodeAuth: Found Username '%s'\n", username);
1218 } else if (!strncmp(item, "realm", ilen)) {
1219 /* white space */
1220
1221 while (xisspace(*p))
1222 p++;
1223
1224 /* quote mark */
1225 p++;
1226
1227 digest_request->realm = xstrndup(p, strchr(p, '"') + 1 - p);
1228
1229 debug(29, 9) ("authDigestDecodeAuth: Found realm '%s'\n", digest_request->realm);
1230 } else if (!strncmp(item, "qop", ilen)) {
1231 /* white space */
1232
1233 while (xisspace(*p))
1234 p++;
1235
1236 if (*p == '\"')
1237 /* quote mark */
1238 p++;
1239
1240 digest_request->qop = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
1241
1242 debug(29, 9) ("authDigestDecodeAuth: Found qop '%s'\n", digest_request->qop);
1243 } else if (!strncmp(item, "algorithm", ilen)) {
1244 /* white space */
1245
1246 while (xisspace(*p))
1247 p++;
1248
1249 if (*p == '\"')
1250 /* quote mark */
1251 p++;
1252
1253 digest_request->algorithm = xstrndup(p, strcspn(p, "\" \t\r\n()<>@,;:\\/[]?={}") + 1);
1254
1255 debug(29, 9) ("authDigestDecodeAuth: Found algorithm '%s'\n", digest_request->algorithm);
1256 } else if (!strncmp(item, "uri", ilen)) {
1257 /* white space */
1258
1259 while (xisspace(*p))
1260 p++;
1261
1262 /* quote mark */
1263 p++;
1264
1265 digest_request->uri = xstrndup(p, strchr(p, '"') + 1 - p);
1266
1267 debug(29, 9) ("authDigestDecodeAuth: Found uri '%s'\n", digest_request->uri);
1268 } else if (!strncmp(item, "nonce", ilen)) {
1269 /* white space */
1270
1271 while (xisspace(*p))
1272 p++;
1273
1274 /* quote mark */
1275 p++;
1276
1277 digest_request->nonceb64 = xstrndup(p, strchr(p, '"') + 1 - p);
1278
1279 debug(29, 9) ("authDigestDecodeAuth: Found nonce '%s'\n", digest_request->nonceb64);
1280 } else if (!strncmp(item, "nc", ilen)) {
1281 /* white space */
1282
1283 while (xisspace(*p))
1284 p++;
1285
1286 xstrncpy(digest_request->nc, p, 9);
1287
1288 debug(29, 9) ("authDigestDecodeAuth: Found noncecount '%s'\n", digest_request->nc);
1289 } else if (!strncmp(item, "cnonce", ilen)) {
1290 /* white space */
1291
1292 while (xisspace(*p))
1293 p++;
1294
1295 /* quote mark */
1296 p++;
1297
1298 digest_request->cnonce = xstrndup(p, strchr(p, '"') + 1 - p);
1299
1300 debug(29, 9) ("authDigestDecodeAuth: Found cnonce '%s'\n", digest_request->cnonce);
1301 } else if (!strncmp(item, "response", ilen)) {
1302 /* white space */
1303
1304 while (xisspace(*p))
1305 p++;
1306
1307 /* quote mark */
1308 p++;
1309
1310 digest_request->response = xstrndup(p, strchr(p, '"') + 1 - p);
1311
1312 debug(29, 9) ("authDigestDecodeAuth: Found response '%s'\n", digest_request->response);
1313 }
2d70df72 1314 }
62e76326 1315
528b2c61 1316 temp.clean();
2d70df72 1317
1318
1319 /* now we validate the data given to us */
1320
74830fc8 1321 /*
1322 * TODO: on invalid parameters we should return 400, not 407.
1323 * Find some clean way of doing this. perhaps return a valid
1324 * struct, and set the direction to clientwards combined with
1325 * a change to the clientwards handling code (ie let the
1326 * clientwards call set the error type (but limited to known
1327 * correct values - 400/401/407
1328 */
2d70df72 1329
1330 /* first the NONCE count */
62e76326 1331
2d70df72 1332 if (digest_request->cnonce && strlen(digest_request->nc) != 8) {
62e76326 1333 debug(29, 4) ("authenticateDigestDecode: nonce count length invalid\n");
1334 authDigestLogUsername(auth_user_request, username);
2d70df72 1335
62e76326 1336 /* we don't need the scheme specific data anymore */
82b045dc 1337 delete digest_request;
62e76326 1338 return;
2d70df72 1339 }
62e76326 1340
2d70df72 1341 /* now the nonce */
1342 nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64);
62e76326 1343
f5292c64 1344 if (!nonce) {
62e76326 1345 /* we couldn't find a matching nonce! */
1346 debug(29, 4) ("authenticateDigestDecode: Unexpected or invalid nonce recieved\n");
1347 authDigestLogUsername(auth_user_request, username);
1348
1349 /* we don't need the scheme specific data anymore */
82b045dc 1350 delete digest_request;
62e76326 1351 return;
2d70df72 1352 }
62e76326 1353
2d70df72 1354 digest_request->nonce = nonce;
2d70df72 1355 authDigestNonceLink(nonce);
1356
62e76326 1357 /* check the qop is what we expected. Note that for compatability with
d205783b 1358 * RFC 2069 we should support a missing qop. Tough. */
62e76326 1359
d205783b 1360 if (!digest_request->qop || strcmp(digest_request->qop, QOP_AUTH)) {
62e76326 1361 /* we recieved a qop option we didn't send */
1362 debug(29, 4) ("authenticateDigestDecode: Invalid qop option recieved\n");
1363 authDigestLogUsername(auth_user_request, username);
1364
1365 /* we don't need the scheme specific data anymore */
82b045dc 1366 delete digest_request;
62e76326 1367 return;
2d70df72 1368 }
62e76326 1369
74830fc8 1370 /* we can't check the URI just yet. We'll check it in the
9bea1d5b 1371 * authenticate phase */
2d70df72 1372
1373 /* is the response the correct length? */
1374
1375 if (!digest_request->response || strlen(digest_request->response) != 32) {
62e76326 1376 debug(29, 4) ("authenticateDigestDecode: Response length invalid\n");
1377 authDigestLogUsername(auth_user_request, username);
2d70df72 1378
62e76326 1379 /* we don't need the scheme specific data anymore */
82b045dc 1380 delete digest_request;
62e76326 1381 return;
2d70df72 1382 }
62e76326 1383
2d70df72 1384 /* do we have a username ? */
1385 if (!username || username[0] == '\0') {
62e76326 1386 debug(29, 4) ("authenticateDigestDecode: Empty or not present username\n");
1387 authDigestLogUsername(auth_user_request, username);
2d70df72 1388
62e76326 1389 /* we don't need the scheme specific data anymore */
82b045dc 1390 delete digest_request;
62e76326 1391 return;
2d70df72 1392 }
62e76326 1393
2d70df72 1394 /* check that we're not being hacked / the username hasn't changed */
e6ccf245 1395 if (nonce->auth_user && strcmp(username, nonce->auth_user->username())) {
62e76326 1396 debug(29, 4) ("authenticateDigestDecode: Username for the nonce does not equal the username for the request\n");
1397 authDigestLogUsername(auth_user_request, username);
2d70df72 1398
62e76326 1399 /* we don't need the scheme specific data anymore */
82b045dc 1400 delete digest_request;
62e76326 1401 return;
2d70df72 1402 }
62e76326 1403
2d70df72 1404 /* if we got a qop, did we get a cnonce or did we get a cnonce wihtout a qop? */
1405 if ((digest_request->qop && !digest_request->cnonce)
62e76326 1406 || (!digest_request->qop && digest_request->cnonce)) {
1407 debug(29, 4) ("authenticateDigestDecode: qop without cnonce, or vice versa!\n");
1408 authDigestLogUsername(auth_user_request, username);
1409
1410 /* we don't need the scheme specific data anymore */
82b045dc 1411 delete digest_request;
62e76326 1412 return;
2d70df72 1413 }
62e76326 1414
2d70df72 1415 /* check the algorithm is present and supported */
d205783b 1416 if (!digest_request->algorithm)
62e76326 1417 digest_request->algorithm = xstrndup("MD5", 4);
d205783b 1418 else if (strcmp(digest_request->algorithm, "MD5")
62e76326 1419 && strcmp(digest_request->algorithm, "MD5-sess")) {
1420 debug(29, 4) ("authenticateDigestDecode: invalid algorithm specified!\n");
1421 authDigestLogUsername(auth_user_request, username);
1422
1423 /* we don't need the scheme specific data anymore */
82b045dc 1424 delete digest_request;
62e76326 1425 return;
2d70df72 1426 }
62e76326 1427
2d70df72 1428 /* the method we'll check at the authenticate step as well */
1429
1430
1431 /* we don't send or parse opaques. Ok so we're flexable ... */
1432
1433 /* find the user */
1434
1435 if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
62e76326 1436 /* the user doesn't exist in the username cache yet */
1437 debug(29, 9) ("authDigestDecodeAuth: Creating new digest user '%s'\n", username);
1438 /* new auth_user */
1439 auth_user = authenticateAuthUserNew("digest");
1440 /* new scheme user data */
82b045dc 1441 digest_user = new digest_user_h;
62e76326 1442 /* save the username */
1443 digest_user->username = username;
1444 /* link the primary struct in */
1445 auth_user->scheme_data = digest_user;
1446 /* set the user type */
1447 auth_user->auth_type = AUTH_DIGEST;
1448 /* this auth_user struct is the one to get added to the
1449 * username cache */
1450 /* store user in hash's */
1451 authenticateUserNameCacheAdd(auth_user);
1452 /*
1453 * Add the digest to the user so we can tell if a hacking
1454 * or spoofing attack is taking place. We do this by assuming
1455 * the user agent won't change user name without warning.
1456 */
1457 authDigestUserLinkNonce(auth_user, nonce);
2d70df72 1458 } else {
62e76326 1459 debug(29, 9) ("authDigestDecodeAuth: Found user '%s' in the user cache as '%p'\n", username, auth_user);
1460 digest_user = static_cast < digest_user_h * >(auth_user->scheme_data);
1461 xfree(username);
2d70df72 1462 }
62e76326 1463
2d70df72 1464 /*link the request and the user */
1465 auth_user_request->auth_user = auth_user;
62e76326 1466
82b045dc 1467 auth_user_request->state(digest_request);
1468
1469 digest_request->authUser (auth_user);
62e76326 1470
2d70df72 1471 /* lock for the request link */
1472 authenticateAuthUserLock(auth_user);
62e76326 1473
2d70df72 1474 node = dlinkNodeNew();
62e76326 1475
2d70df72 1476 dlinkAdd(auth_user_request, node, &auth_user->requests);
1477
33272404 1478 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 1479 digest_user->username, digest_request->realm,
1480 digest_request->qop, digest_request->algorithm,
1481 digest_request->uri, digest_request->nonceb64,
1482 digest_request->nc, digest_request->cnonce, digest_request->response, nonce);
2d70df72 1483
1484 return;
1485}
1486
1487/* send the initial data to a digest authenticator module */
1488static void
1489authenticateDigestStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
1490{
e6ccf245 1491 DigestAuthenticateStateData *r = NULL;
2d70df72 1492 char buf[8192];
1493 digest_request_h *digest_request;
1494 digest_user_h *digest_user;
1495 assert(auth_user_request);
1496 assert(handler);
1497 assert(auth_user_request->auth_user->auth_type == AUTH_DIGEST);
1498 assert(auth_user_request->auth_user->scheme_data != NULL);
82b045dc 1499 digest_request = dynamic_cast < digest_request_h * >(auth_user_request->state());
1500 assert(digest_request);
2f44bd34 1501 digest_user = static_cast < digest_user_h * >(auth_user_request->auth_user->scheme_data);
2d70df72 1502 debug(29, 9) ("authenticateStart: '\"%s\":\"%s\"'\n", digest_user->username,
62e76326 1503 digest_request->realm);
1504
2d70df72 1505 if (digestConfig->authenticate == NULL) {
62e76326 1506 handler(data, NULL);
1507 return;
2d70df72 1508 }
62e76326 1509
e6ccf245 1510 r = cbdataAlloc(DigestAuthenticateStateData);
2d70df72 1511 r->handler = handler;
fa80a8ef 1512 r->data = cbdataReference(data);
2d70df72 1513 r->auth_user_request = auth_user_request;
1514 snprintf(buf, 8192, "\"%s\":\"%s\"\n", digest_user->username, digest_request->realm);
1515 helperSubmit(digestauthenticators, buf, authenticateDigestHandleReply, r);
1516}
82b045dc 1517
1518
bf5113eb 1519MemPool (*digest_user_h::Pool)(NULL);
82b045dc 1520void *
1521digest_user_h::operator new (size_t byteCount)
1522{
1523 /* derived classes with different sizes must implement their own new */
1524 assert (byteCount == sizeof (digest_user_h));
1525
1526 if (!Pool)
1527 Pool = memPoolCreate("digest_user_h", sizeof (digest_user_h));
1528
1529 return memPoolAlloc(Pool);
1530}
1531
1532void
1533digest_user_h::operator delete (void *address)
1534{
1535 memPoolFree (Pool, address);
1536}
1537
82b045dc 1538digest_user_h::digest_user_h () : username (NULL), HA1created (0)
1539{}
1540
bf5113eb 1541MemPool (*digest_request_h::Pool)(NULL);
82b045dc 1542void *
1543digest_request_h::operator new (size_t byteCount)
1544{
1545 /* derived classes with different sizes must implement their own new */
1546 assert (byteCount == sizeof (digest_request_h));
1547
1548 if (!Pool)
1549 Pool = memPoolCreate("digest_request_h", sizeof (digest_request_h));
1550
1551 return memPoolAlloc(Pool);
1552}
1553
1554void
1555digest_request_h::operator delete (void *address)
1556{
1557 memPoolFree (Pool, address);
1558}
1559
82b045dc 1560digest_request_h::digest_request_h () : theUser (NULL)
1561 , credentials_ok (Unchecked)
1562{}
1563
1564digest_request_h::digest_request_h (auth_user_t *aUser) : theUser (aUser)
1565 , credentials_ok (Unchecked)
1566{
1567 authenticateAuthUserLock(theUser);
1568}
1569
1570auth_user_t *
1571digest_request_h::authUser() const
1572{
1573 return theUser;
1574}
1575
1576void
1577digest_request_h::authUser(auth_user_t *aUser)
1578{
1579 assert (!authUser());
1580 authenticateAuthUserLock(aUser);
1581 theUser = aUser;
1582}
1583
1584digest_request_h::CredentialsState
1585digest_request_h::credentials() const
1586{
1587 return credentials_ok;
1588
1589}
1590
1591void
1592digest_request_h::credentials(CredentialsState newCreds)
1593{
1594 credentials_ok = newCreds;
1595}