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