]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/digest/auth_digest.cc
Portability fix: u_int*_t types are deprecated, replaced with uint*_t
[thirdparty/squid.git] / src / auth / digest / auth_digest.cc
CommitLineData
2d70df72 1/*
262a0e14 2 * $Id$
2d70df72 3 *
4 * DEBUG: section 29 Authenticator
5 * AUTHOR: Robert Collins
6 *
7 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
8 * ----------------------------------------------------------
9 *
10 * Squid is the result of efforts by numerous individuals from the
11 * Internet community. Development is led by Duane Wessels of the
12 * National Laboratory for Applied Network Research and funded by the
13 * National Science Foundation. Squid is Copyrighted (C) 1998 by
14 * the Regents of the University of California. Please see the
15 * COPYRIGHT file for full details. Squid incorporates software
16 * developed and/or copyrighted by other sources. Please see the
17 * CREDITS file for full details.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
26ac0430 23 *
2d70df72 24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
26ac0430 28 *
2d70df72 29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35/* The functions in this file handle authentication.
36 * They DO NOT perform access control or auditing.
37 * See acl.c for access control and client_side.c for auditing */
38
39
40#include "squid.h"
41#include "rfc2617.h"
5817ee13 42#include "auth/digest/auth_digest.h"
3ad63615 43#include "auth/Gadgets.h"
a553a5a3 44#include "event.h"
62ee09ca 45#include "CacheManager.h"
e6ccf245 46#include "Store.h"
a2ac85d9 47#include "HttpRequest.h"
924f73bc 48#include "HttpReply.h"
d295d770 49#include "wordlist.h"
cc192b50 50#include "SquidTime.h"
f5691f9c 51/* TODO don't include this */
5817ee13 52#include "auth/digest/digestScheme.h"
928f3421 53#include "auth/digest/digestUserRequest.h"
c78aa667 54
2d70df72 55/* Digest Scheme */
56
2d70df72 57static AUTHSSTATS authenticateDigestStats;
2d70df72 58
928f3421 59helper *digestauthenticators = NULL;
2d70df72 60
61static hash_table *digest_nonce_cache;
62
2d70df72 63static int authdigest_initialised = 0;
a3efa961 64static MemAllocator *digest_nonce_pool = NULL;
2d70df72 65
ea0695f2 66// CBDATA_TYPE(DigestAuthenticateStateData);
2d70df72 67
9abd1514 68enum http_digest_attr_type {
d6b7a3c4 69 DIGEST_USERNAME,
cb14509d
HN
70 DIGEST_REALM,
71 DIGEST_QOP,
72 DIGEST_ALGORITHM,
73 DIGEST_URI,
74 DIGEST_NONCE,
75 DIGEST_NC,
76 DIGEST_CNONCE,
77 DIGEST_RESPONSE,
78 DIGEST_ENUM_END
79};
80
81static const HttpHeaderFieldAttrs DigestAttrs[DIGEST_ENUM_END] = {
82 {"username", (http_hdr_type)DIGEST_USERNAME},
83 {"realm", (http_hdr_type)DIGEST_REALM},
84 {"qop", (http_hdr_type)DIGEST_QOP},
85 {"algorithm", (http_hdr_type)DIGEST_ALGORITHM},
86 {"uri", (http_hdr_type)DIGEST_URI},
87 {"nonce", (http_hdr_type)DIGEST_NONCE},
88 {"nc", (http_hdr_type)DIGEST_NC},
89 {"cnonce", (http_hdr_type)DIGEST_CNONCE},
90 {"response", (http_hdr_type)DIGEST_RESPONSE},
91};
92
93static HttpHeaderFieldInfo *DigestFieldsInfo = NULL;
94
2d70df72 95/*
96 *
97 * Nonce Functions
98 *
99 */
100
101static void authenticateDigestNonceCacheCleanup(void *data);
102static digest_nonce_h *authenticateDigestNonceFindNonce(const char *nonceb64);
c193c972 103static digest_nonce_h *authenticateDigestNonceNew(void);
c78aa667 104static void authenticateDigestNonceDelete(digest_nonce_h * nonce);
c193c972 105static void authenticateDigestNonceSetup(void);
106static void authenticateDigestNonceShutdown(void);
107static void authenticateDigestNonceReconfigure(void);
c78aa667 108static int authDigestNonceIsStale(digest_nonce_h * nonce);
109static void authDigestNonceEncode(digest_nonce_h * nonce);
c78aa667 110static void authDigestNonceLink(digest_nonce_h * nonce);
c78aa667 111#if NOT_USED
112static int authDigestNonceLinks(digest_nonce_h * nonce);
113#endif
114static void authDigestNonceUserUnlink(digest_nonce_h * nonce);
115static void authDigestNoncePurge(digest_nonce_h * nonce);
2d70df72 116
c78aa667 117static void
2d70df72 118authDigestNonceEncode(digest_nonce_h * nonce)
119{
120 if (!nonce)
62e76326 121 return;
122
4a8b20e8 123 if (nonce->key)
62e76326 124 xfree(nonce->key);
125
4a8b20e8 126 nonce->key = xstrdup(base64_encode_bin((char *) &(nonce->noncedata), sizeof(digest_nonce_data)));
2d70df72 127}
128
c78aa667 129static digest_nonce_h *
c193c972 130authenticateDigestNonceNew(void)
2d70df72 131{
b001e822 132 digest_nonce_h *newnonce = static_cast < digest_nonce_h * >(digest_nonce_pool->alloc());
2d70df72 133 digest_nonce_h *temp;
134
62e76326 135 /* NONCE CREATION - NOTES AND REASONING. RBC 20010108
136 * === EXCERPT FROM RFC 2617 ===
137 * The contents of the nonce are implementation dependent. The quality
138 * of the implementation depends on a good choice. A nonce might, for
139 * example, be constructed as the base 64 encoding of
26ac0430 140 *
62e76326 141 * time-stamp H(time-stamp ":" ETag ":" private-key)
26ac0430 142 *
62e76326 143 * where time-stamp is a server-generated time or other non-repeating
144 * value, ETag is the value of the HTTP ETag header associated with
145 * the requested entity, and private-key is data known only to the
146 * server. With a nonce of this form a server would recalculate the
147 * hash portion after receiving the client authentication header and
148 * reject the request if it did not match the nonce from that header
149 * or if the time-stamp value is not recent enough. In this way the
150 * server can limit the time of the nonce's validity. The inclusion of
151 * the ETag prevents a replay request for an updated version of the
152 * resource. (Note: including the IP address of the client in the
153 * nonce would appear to offer the server the ability to limit the
154 * reuse of the nonce to the same client that originally got it.
155 * However, that would break proxy farms, where requests from a single
156 * user often go through different proxies in the farm. Also, IP
157 * address spoofing is not that hard.)
158 * ====
26ac0430 159 *
62e76326 160 * Now for my reasoning:
161 * We will not accept a unrecognised nonce->we have all recognisable
82b045dc 162 * nonces stored. If we send out unique base64 encodings we guarantee
62e76326 163 * that a given nonce applies to only one user (barring attacks or
164 * really bad timing with expiry and creation). Using a random
165 * component in the nonce allows us to loop to find a unique nonce.
166 * We use H(nonce_data) so the nonce is meaningless to the reciever.
167 * So our nonce looks like base64(H(timestamp,pointertohash,randomdata))
168 * And even if our randomness is not very random (probably due to
169 * bad coding on my part) we don't really care - the timestamp and
82b045dc 170 * memory pointer also guarantee local uniqueness in the input to the hash
171 * function.
62e76326 172 */
2d70df72 173
174 /* create a new nonce */
175 newnonce->nc = 0;
176 newnonce->flags.valid = 1;
177 newnonce->noncedata.self = newnonce;
178 newnonce->noncedata.creationtime = current_time.tv_sec;
b600a19d 179 newnonce->noncedata.randomdata = squid_random();
2d70df72 180
181 authDigestNonceEncode(newnonce);
74830fc8 182 /*
183 * loop until we get a unique nonce. The nonce creation must
184 * have a random factor
185 */
62e76326 186
2f44bd34 187 while ((temp = authenticateDigestNonceFindNonce((char const *) (newnonce->key)))) {
62e76326 188 /* create a new nonce */
189 newnonce->noncedata.randomdata = squid_random();
190 authDigestNonceEncode(newnonce);
2d70df72 191 }
62e76326 192
4a8b20e8 193 hash_join(digest_nonce_cache, newnonce);
2d70df72 194 /* the cache's link */
195 authDigestNonceLink(newnonce);
196 newnonce->flags.incache = 1;
4a7a3d56 197 debugs(29, 5, "authenticateDigestNonceNew: created nonce " << newnonce << " at " << newnonce->noncedata.creationtime);
2d70df72 198 return newnonce;
199}
200
c78aa667 201static void
2d70df72 202authenticateDigestNonceDelete(digest_nonce_h * nonce)
203{
204 if (nonce) {
62e76326 205 assert(nonce->references == 0);
2d70df72 206#if UNREACHABLECODE
62e76326 207
208 if (nonce->flags.incache)
209 hash_remove_link(digest_nonce_cache, nonce);
210
2d70df72 211#endif
62e76326 212
213 assert(nonce->flags.incache == 0);
214
215 safe_free(nonce->key);
216
dc47f531 217 digest_nonce_pool->freeOne(nonce);
2d70df72 218 }
219}
220
c78aa667 221static void
c193c972 222authenticateDigestNonceSetup(void)
2d70df72 223{
224 if (!digest_nonce_pool)
04eb0689 225 digest_nonce_pool = memPoolCreate("Digest Scheme nonce's", sizeof(digest_nonce_h));
62e76326 226
2d70df72 227 if (!digest_nonce_cache) {
30abd221 228 digest_nonce_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
62e76326 229 assert(digest_nonce_cache);
5817ee13 230 eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
2d70df72 231 }
232}
233
c78aa667 234static void
c193c972 235authenticateDigestNonceShutdown(void)
2d70df72 236{
62e76326 237 /*
2d70df72 238 * We empty the cache of any nonces left in there.
239 */
240 digest_nonce_h *nonce;
62e76326 241
2d70df72 242 if (digest_nonce_cache) {
bf8fe701 243 debugs(29, 2, "authenticateDigestNonceShutdown: Shutting down nonce cache ");
62e76326 244 hash_first(digest_nonce_cache);
245
246 while ((nonce = ((digest_nonce_h *) hash_next(digest_nonce_cache)))) {
247 assert(nonce->flags.incache);
248 authDigestNoncePurge(nonce);
249 }
2d70df72 250 }
62e76326 251
2f44bd34 252#if DEBUGSHUTDOWN
2d70df72 253 if (digest_nonce_pool) {
b001e822 254 delete digest_nonce_pool;
255 digest_nonce_pool = NULL;
2d70df72 256 }
62e76326 257
2f44bd34 258#endif
bf8fe701 259 debugs(29, 2, "authenticateDigestNonceShutdown: Nonce cache shutdown");
2d70df72 260}
261
c78aa667 262static void
c193c972 263authenticateDigestNonceReconfigure(void)
62e76326 264{}
2d70df72 265
c78aa667 266static void
2d70df72 267authenticateDigestNonceCacheCleanup(void *data)
268{
269 /*
74830fc8 270 * We walk the hash by nonceb64 as that is the unique key we
271 * use. For big hash tables we could consider stepping through
272 * the cache, 100/200 entries at a time. Lets see how it flies
273 * first.
2d70df72 274 */
275 digest_nonce_h *nonce;
bf8fe701 276 debugs(29, 3, "authenticateDigestNonceCacheCleanup: Cleaning the nonce cache now");
4a7a3d56 277 debugs(29, 3, "authenticateDigestNonceCacheCleanup: Current time: " << current_time.tv_sec);
2d70df72 278 hash_first(digest_nonce_cache);
62e76326 279
2d70df72 280 while ((nonce = ((digest_nonce_h *) hash_next(digest_nonce_cache)))) {
bf8fe701 281 debugs(29, 3, "authenticateDigestNonceCacheCleanup: nonce entry : " << nonce << " '" << (char *) nonce->key << "'");
4a7a3d56 282 debugs(29, 4, "authenticateDigestNonceCacheCleanup: Creation time: " << nonce->noncedata.creationtime);
62e76326 283
284 if (authDigestNonceIsStale(nonce)) {
bf8fe701 285 debugs(29, 4, "authenticateDigestNonceCacheCleanup: Removing nonce " << (char *) nonce->key << " from cache due to timeout.");
62e76326 286 assert(nonce->flags.incache);
287 /* invalidate nonce so future requests fail */
288 nonce->flags.valid = 0;
289 /* if it is tied to a auth_user, remove the tie */
290 authDigestNonceUserUnlink(nonce);
291 authDigestNoncePurge(nonce);
292 }
2d70df72 293 }
62e76326 294
bf8fe701 295 debugs(29, 3, "authenticateDigestNonceCacheCleanup: Finished cleaning the nonce cache.");
62e76326 296
5817ee13
AJ
297 if (static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->active())
298 eventAdd("Digest none cache maintenance", authenticateDigestNonceCacheCleanup, NULL, static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->nonceGCInterval, 1);
2d70df72 299}
300
c78aa667 301static void
2d70df72 302authDigestNonceLink(digest_nonce_h * nonce)
303{
304 assert(nonce != NULL);
305 nonce->references++;
bf8fe701 306 debugs(29, 9, "authDigestNonceLink: nonce '" << nonce << "' now at '" << nonce->references << "'.");
2d70df72 307}
308
c78aa667 309#if NOT_USED
310static int
2d70df72 311authDigestNonceLinks(digest_nonce_h * nonce)
312{
313 if (!nonce)
62e76326 314 return -1;
315
2d70df72 316 return nonce->references;
317}
62e76326 318
c78aa667 319#endif
2d70df72 320
928f3421 321void
2d70df72 322authDigestNonceUnlink(digest_nonce_h * nonce)
323{
324 assert(nonce != NULL);
62e76326 325
2d70df72 326 if (nonce->references > 0) {
62e76326 327 nonce->references--;
2d70df72 328 } else {
bf8fe701 329 debugs(29, 1, "authDigestNonceUnlink; Attempt to lower nonce " << nonce << " refcount below 0!");
2d70df72 330 }
62e76326 331
bf8fe701 332 debugs(29, 9, "authDigestNonceUnlink: nonce '" << nonce << "' now at '" << nonce->references << "'.");
62e76326 333
2d70df72 334 if (nonce->references == 0)
62e76326 335 authenticateDigestNonceDelete(nonce);
2d70df72 336}
337
928f3421
AJ
338const char *
339authenticateDigestNonceNonceb64(const digest_nonce_h * nonce)
2d70df72 340{
341 if (!nonce)
62e76326 342 return NULL;
343
2f44bd34 344 return (char const *) nonce->key;
2d70df72 345}
346
c78aa667 347static digest_nonce_h *
2d70df72 348authenticateDigestNonceFindNonce(const char *nonceb64)
349{
350 digest_nonce_h *nonce = NULL;
62e76326 351
2d70df72 352 if (nonceb64 == NULL)
62e76326 353 return NULL;
354
26ac0430 355 debugs(29, 9, "authDigestNonceFindNonce:looking for nonceb64 '" << nonceb64 << "' in the nonce cache.");
62e76326 356
2f44bd34 357 nonce = static_cast < digest_nonce_h * >(hash_lookup(digest_nonce_cache, nonceb64));
62e76326 358
e6ccf245 359 if ((nonce == NULL) || (strcmp(authenticateDigestNonceNonceb64(nonce), nonceb64)))
62e76326 360 return NULL;
361
bf8fe701 362 debugs(29, 9, "authDigestNonceFindNonce: Found nonce '" << nonce << "'");
62e76326 363
2d70df72 364 return nonce;
365}
366
928f3421 367int
2d70df72 368authDigestNonceIsValid(digest_nonce_h * nonce, char nc[9])
369{
d205783b 370 unsigned long intnc;
2d70df72 371 /* do we have a nonce ? */
62e76326 372
2d70df72 373 if (!nonce)
62e76326 374 return 0;
375
d205783b 376 intnc = strtol(nc, NULL, 16);
62e76326 377
f5292c64 378 /* has it already been invalidated ? */
379 if (!nonce->flags.valid) {
bf8fe701 380 debugs(29, 4, "authDigestNonceIsValid: Nonce already invalidated");
f5292c64 381 return 0;
382 }
383
384 /* is the nonce-count ok ? */
5817ee13 385 if (!static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->CheckNonceCount) {
f5292c64 386 nonce->nc++;
387 return -1; /* forced OK by configuration */
388 }
389
5817ee13 390 if ((static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->NonceStrictness && intnc != nonce->nc + 1) ||
62e76326 391 intnc < nonce->nc + 1) {
bf8fe701 392 debugs(29, 4, "authDigestNonceIsValid: Nonce count doesn't match");
62e76326 393 nonce->flags.valid = 0;
394 return 0;
2d70df72 395 }
62e76326 396
2d70df72 397 /* seems ok */
d205783b 398 /* increment the nonce count - we've already checked that intnc is a
399 * valid representation for us, so we don't need the test here.
400 */
401 nonce->nc = intnc;
62e76326 402
2d70df72 403 return -1;
404}
405
c78aa667 406static int
2d70df72 407authDigestNonceIsStale(digest_nonce_h * nonce)
408{
409 /* do we have a nonce ? */
62e76326 410
2d70df72 411 if (!nonce)
62e76326 412 return -1;
413
2d70df72 414 /* has it's max duration expired? */
5817ee13 415 if (nonce->noncedata.creationtime + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration < current_time.tv_sec) {
bf8fe701 416 debugs(29, 4, "authDigestNonceIsStale: Nonce is too old. " <<
4a7a3d56 417 nonce->noncedata.creationtime << " " <<
5817ee13 418 static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration << " " <<
4a7a3d56 419 current_time.tv_sec);
bf8fe701 420
62e76326 421 nonce->flags.valid = 0;
422 return -1;
2d70df72 423 }
62e76326 424
2d70df72 425 if (nonce->nc > 99999998) {
bf8fe701 426 debugs(29, 4, "authDigestNonceIsStale: Nonce count overflow");
62e76326 427 nonce->flags.valid = 0;
428 return -1;
2d70df72 429 }
62e76326 430
5817ee13 431 if (nonce->nc > static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses) {
bf8fe701 432 debugs(29, 4, "authDigestNoncelastRequest: Nonce count over user limit");
62e76326 433 nonce->flags.valid = 0;
434 return -1;
2d70df72 435 }
62e76326 436
2d70df72 437 /* seems ok */
438 return 0;
439}
440
928f3421
AJ
441/**
442 * \retval 0 the digest is not stale yet
443 * \retval -1 the digest will be stale on the next request
444 */
445const int
2d70df72 446authDigestNonceLastRequest(digest_nonce_h * nonce)
447{
448 if (!nonce)
62e76326 449 return -1;
450
2d70df72 451 if (nonce->nc == 99999997) {
bf8fe701 452 debugs(29, 4, "authDigestNoncelastRequest: Nonce count about to overflow");
62e76326 453 return -1;
2d70df72 454 }
62e76326 455
5817ee13 456 if (nonce->nc >= static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxuses - 1) {
bf8fe701 457 debugs(29, 4, "authDigestNoncelastRequest: Nonce count about to hit user limit");
62e76326 458 return -1;
2d70df72 459 }
62e76326 460
2d70df72 461 /* and other tests are possible. */
462 return 0;
463}
464
c78aa667 465static void
2d70df72 466authDigestNoncePurge(digest_nonce_h * nonce)
467{
468 if (!nonce)
62e76326 469 return;
470
2d70df72 471 if (!nonce->flags.incache)
62e76326 472 return;
473
4a8b20e8 474 hash_remove_link(digest_nonce_cache, nonce);
62e76326 475
2d70df72 476 nonce->flags.incache = 0;
62e76326 477
2d70df72 478 /* the cache's link */
479 authDigestNonceUnlink(nonce);
480}
481
62e76326 482/* USER related functions */
56a49fda 483static AuthUser::Pointer
2d70df72 484authDigestUserFindUsername(const char *username)
485{
e1f7507e 486 AuthUserHashPointer *usernamehash;
e1f7507e 487 debugs(29, 9, HERE << "Looking for user '" << username << "'");
62e76326 488
2f44bd34 489 if (username && (usernamehash = static_cast < auth_user_hash_pointer * >(hash_lookup(proxy_auth_username_cache, username)))) {
56a49fda
AJ
490 while ((usernamehash->user()->auth_type != AUTH_DIGEST) && (usernamehash->next))
491 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
62e76326 492
f5691f9c 493 if (usernamehash->user()->auth_type == AUTH_DIGEST) {
56a49fda 494 return usernamehash->user();
62e76326 495 }
2d70df72 496 }
62e76326 497
2d70df72 498 return NULL;
499}
500
0bcb6908
AJ
501void
502AuthDigestConfig::rotateHelpers()
503{
504 /* schedule closure of existing helpers */
505 if (digestauthenticators) {
506 helperShutdown(digestauthenticators);
507 }
508
509 /* NP: dynamic helper restart will ensure they start up again as needed. */
510}
511
e1f7507e 512/** delete the digest request structure. Does NOT delete related structures */
f5691f9c 513void
514digestScheme::done()
2d70df72 515{
e1f7507e 516 /** \todo this should be a Config call. */
2d70df72 517
2d70df72 518 if (digestauthenticators)
62e76326 519 helperShutdown(digestauthenticators);
520
987e0099 521 if (DigestFieldsInfo) {
a0133f10
A
522 httpHeaderDestroyFieldsInfo(DigestFieldsInfo, DIGEST_ENUM_END);
523 DigestFieldsInfo = NULL;
987e0099 524 }
cb14509d 525
2d70df72 526 authdigest_initialised = 0;
62e76326 527
2d70df72 528 if (!shutting_down) {
62e76326 529 authenticateDigestNonceReconfigure();
530 return;
2d70df72 531 }
62e76326 532
48d54e4d
AJ
533 delete digestauthenticators;
534 digestauthenticators = NULL;
62e76326 535
56a49fda 536 PurgeCredentialsCache();
2d70df72 537 authenticateDigestNonceShutdown();
bf8fe701 538 debugs(29, 2, "authenticateDigestDone: Digest authentication shut down.");
5817ee13
AJ
539
540 /* clear the global handle to this scheme. */
541 _instance = NULL;
2d70df72 542}
543
f5691f9c 544void
545AuthDigestConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
2d70df72 546{
f5691f9c 547 wordlist *list = authenticate;
bf8fe701 548 debugs(29, 9, "authDigestCfgDump: Dumping configuration");
2d70df72 549 storeAppendPrintf(entry, "%s %s", name, "digest");
62e76326 550
2d70df72 551 while (list != NULL) {
62e76326 552 storeAppendPrintf(entry, " %s", list->key);
553 list = list->next;
2d70df72 554 }
62e76326 555
404cfda1 556 storeAppendPrintf(entry, "\n%s %s realm %s\n%s %s children %d startup=%d idle=%d concurrency=%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",
f5691f9c 557 name, "digest", digestAuthRealm,
404cfda1 558 name, "digest", authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency,
f5691f9c 559 name, "digest", noncemaxuses,
560 name, "digest", (int) noncemaxduration,
561 name, "digest", (int) nonceGCInterval);
2d70df72 562}
563
f5691f9c 564bool
565AuthDigestConfig::active() const
2d70df72 566{
f5691f9c 567 return authdigest_initialised == 1;
2d70df72 568}
62e76326 569
f5691f9c 570bool
571AuthDigestConfig::configured() const
2d70df72 572{
f5691f9c 573 if ((authenticate != NULL) &&
48d54e4d 574 (authenticateChildren.n_max != 0) &&
f5691f9c 575 (digestAuthRealm != NULL) && (noncemaxduration > -1))
576 return true;
62e76326 577
f5691f9c 578 return false;
2d70df72 579}
580
2d70df72 581/* add the [www-|Proxy-]authenticate header on a 407 or 401 reply */
582void
a33a428a 583AuthDigestConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
2d70df72 584{
f5691f9c 585 if (!authenticate)
82b045dc 586 return;
587
a598b010 588 int stale = 0;
62e76326 589
a33a428a 590 if (auth_user_request != NULL) {
f5691f9c 591 AuthDigestUserRequest *digest_request;
a33a428a 592 digest_request = dynamic_cast<AuthDigestUserRequest*>(auth_user_request.getRaw());
7da7b01f 593 assert (digest_request != NULL);
62e76326 594
9f709824 595 stale = !digest_request->flags.invalid_password;
2d70df72 596 }
82b045dc 597
598 /* on a 407 or 401 we always use a new nonce */
599 digest_nonce_h *nonce = authenticateDigestNonceNew();
600
18ec8500 601 debugs(29, 9, "authenticateFixHeader: Sending type:" << hdrType <<
bf8fe701 602 " header: 'Digest realm=\"" << digestAuthRealm << "\", nonce=\"" <<
603 authenticateDigestNonceNonceb64(nonce) << "\", qop=\"" << QOP_AUTH <<
604 "\", stale=" << (stale ? "true" : "false"));
82b045dc 605
606 /* in the future, for WWW auth we may want to support the domain entry */
18ec8500 607 httpHeaderPutStrf(&rep->header, hdrType, "Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", stale=%s", digestAuthRealm, authenticateDigestNonceNonceb64(nonce), QOP_AUTH, stale ? "true" : "false");
2d70df72 608}
609
f5691f9c 610DigestUser::~DigestUser()
2d70df72 611{
82b045dc 612 dlink_node *link, *tmplink;
613 link = nonces.head;
62e76326 614
2d70df72 615 while (link) {
62e76326 616 tmplink = link;
617 link = link->next;
82b045dc 618 dlinkDelete(tmplink, &nonces);
62e76326 619 authDigestNoncePurge(static_cast < digest_nonce_h * >(tmplink->data));
620 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
621 dlinkNodeDelete(tmplink);
2d70df72 622 }
2d70df72 623}
624
56a49fda
AJ
625int32_t
626DigestUser::ttl() const
627{
628 int32_t global_ttl = static_cast<int32_t>(expiretime - squid_curtime + Config.authenticateTTL);
629
630 /* find the longest lasting nonce. */
631 int32_t latest_nonce = -1;
632 dlink_node *link = nonces.head;
633 while (link) {
634 digest_nonce_h *nonce = static_cast<digest_nonce_h *>(link->data);
635 if (nonce->flags.valid && nonce->noncedata.creationtime > latest_nonce)
636 latest_nonce = nonce->noncedata.creationtime;
637
638 link = link->next;
639 }
640 if (latest_nonce == -1)
641 return min(-1, global_ttl);
642
643 int32_t nonce_ttl = latest_nonce - current_time.tv_sec + static_cast<AuthDigestConfig*>(AuthConfig::Find("digest"))->noncemaxduration;
644
645 return min(nonce_ttl, global_ttl);
646}
647
2d70df72 648/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
649 * config file */
f5691f9c 650void
651AuthDigestConfig::init(AuthConfig * scheme)
2d70df72 652{
f5691f9c 653 if (authenticate) {
6d97f5f1 654 DigestFieldsInfo = httpHeaderBuildFieldsInfo(DigestAttrs, DIGEST_ENUM_END);
62e76326 655 authenticateDigestNonceSetup();
656 authdigest_initialised = 1;
657
658 if (digestauthenticators == NULL)
48d54e4d 659 digestauthenticators = new helper("digestauthenticator");
62e76326 660
f5691f9c 661 digestauthenticators->cmdline = authenticate;
62e76326 662
48d54e4d 663 digestauthenticators->childs = authenticateChildren;
62e76326 664
665 digestauthenticators->ipc_type = IPC_STREAM;
666
667 helperOpenServers(digestauthenticators);
668
928f3421 669 CBDATA_INIT_TYPE(authenticateStateData);
2d70df72 670 }
671}
672
62ee09ca 673void
15fab853 674AuthDigestConfig::registerWithCacheManager(void)
62ee09ca 675{
15fab853 676 CacheManager::GetInstance()->
26ac0430
AJ
677 registerAction("digestauthenticator",
678 "Digest User Authenticator Stats",
679 authenticateDigestStats, 0, 1);
62ee09ca 680}
2d70df72 681
682/* free any allocated configuration details */
683void
f5691f9c 684AuthDigestConfig::done()
2d70df72 685{
f5691f9c 686 if (authenticate)
687 wordlistDestroy(&authenticate);
62e76326 688
f5691f9c 689 safe_free(digestAuthRealm);
690}
62e76326 691
ea0695f2 692AuthDigestConfig::AuthDigestConfig()
f5691f9c 693{
694 /* TODO: move into initialisation list */
f5691f9c 695 /* 5 minutes */
696 nonceGCInterval = 5 * 60;
697 /* 30 minutes */
698 noncemaxduration = 30 * 60;
699 /* 50 requests */
700 noncemaxuses = 50;
701 /* Not strict nonce count behaviour */
702 NonceStrictness = 0;
703 /* Verify nonce count */
704 CheckNonceCount = 1;
2d70df72 705}
706
f5691f9c 707void
708AuthDigestConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
2d70df72 709{
2d70df72 710 if (strcasecmp(param_str, "program") == 0) {
f5691f9c 711 if (authenticate)
712 wordlistDestroy(&authenticate);
62e76326 713
f5691f9c 714 parse_wordlist(&authenticate);
62e76326 715
42900318 716 requirePathnameExists("auth_param digest program", authenticate->key);
2d70df72 717 } else if (strcasecmp(param_str, "children") == 0) {
48d54e4d 718 authenticateChildren.parseConfig();
2d70df72 719 } else if (strcasecmp(param_str, "realm") == 0) {
f5691f9c 720 parse_eol(&digestAuthRealm);
2d70df72 721 } else if (strcasecmp(param_str, "nonce_garbage_interval") == 0) {
f5691f9c 722 parse_time_t(&nonceGCInterval);
2d70df72 723 } else if (strcasecmp(param_str, "nonce_max_duration") == 0) {
f5691f9c 724 parse_time_t(&noncemaxduration);
2d70df72 725 } else if (strcasecmp(param_str, "nonce_max_count") == 0) {
f5691f9c 726 parse_int((int *) &noncemaxuses);
d205783b 727 } else if (strcasecmp(param_str, "nonce_strictness") == 0) {
f5691f9c 728 parse_onoff(&NonceStrictness);
f5292c64 729 } else if (strcasecmp(param_str, "check_nonce_count") == 0) {
f5691f9c 730 parse_onoff(&CheckNonceCount);
f5292c64 731 } else if (strcasecmp(param_str, "post_workaround") == 0) {
f5691f9c 732 parse_onoff(&PostWorkaround);
f741d2f6
HN
733 } else if (strcasecmp(param_str, "utf8") == 0) {
734 parse_onoff(&utf8);
2d70df72 735 } else {
bf8fe701 736 debugs(29, 0, "unrecognised digest auth scheme parameter '" << param_str << "'");
2d70df72 737 }
738}
739
f5691f9c 740const char *
741AuthDigestConfig::type() const
742{
5817ee13 743 return digestScheme::GetInstance()->type();
f5691f9c 744}
745
2d70df72 746
747static void
748authenticateDigestStats(StoreEntry * sentry)
749{
9522b380 750 helperStats(sentry, digestauthenticators, "Digest Authenticator Statistics");
2d70df72 751}
752
753/* NonceUserUnlink: remove the reference to auth_user and unlink the node from the list */
754
c78aa667 755static void
2d70df72 756authDigestNonceUserUnlink(digest_nonce_h * nonce)
757{
56a49fda 758 DigestUser *digest_user;
2d70df72 759 dlink_node *link, *tmplink;
62e76326 760
2d70df72 761 if (!nonce)
62e76326 762 return;
763
f5691f9c 764 if (!nonce->user)
62e76326 765 return;
766
f5691f9c 767 digest_user = nonce->user;
62e76326 768
769 /* unlink from the user list. Yes we're crossing structures but this is the only
2d70df72 770 * time this code is needed
771 */
772 link = digest_user->nonces.head;
62e76326 773
2d70df72 774 while (link) {
62e76326 775 tmplink = link;
776 link = link->next;
777
778 if (tmplink->data == nonce) {
779 dlinkDelete(tmplink, &digest_user->nonces);
780 authDigestNonceUnlink(static_cast < digest_nonce_h * >(tmplink->data));
781 dlinkNodeDelete(tmplink);
782 link = NULL;
783 }
2d70df72 784 }
62e76326 785
f5691f9c 786 /* this reference to user was not locked because freeeing the user frees
26ac0430 787 * the nonce too.
2d70df72 788 */
f5691f9c 789 nonce->user = NULL;
2d70df72 790}
791
792/* authDigestUserLinkNonce: add a nonce to a given user's struct */
793
c78aa667 794static void
f5691f9c 795authDigestUserLinkNonce(DigestUser * user, digest_nonce_h * nonce)
2d70df72 796{
797 dlink_node *node;
56a49fda 798 DigestUser *digest_user;
62e76326 799
f5691f9c 800 if (!user || !nonce)
62e76326 801 return;
802
f5691f9c 803 digest_user = user;
62e76326 804
2d70df72 805 node = digest_user->nonces.head;
62e76326 806
2d70df72 807 while (node && (node->data != nonce))
62e76326 808 node = node->next;
809
2d70df72 810 if (node)
62e76326 811 return;
812
2d70df72 813 node = dlinkNodeNew();
62e76326 814
2d70df72 815 dlinkAddTail(nonce, node, &digest_user->nonces);
62e76326 816
2d70df72 817 authDigestNonceLink(nonce);
62e76326 818
2d70df72 819 /* ping this nonce to this auth user */
4e9d4067 820 assert((nonce->user == NULL) || (nonce->user == user));
62e76326 821
f5691f9c 822 /* we don't lock this reference because removing the user removes the
2d70df72 823 * hash too. Of course if that changes we're stuffed so read the code huh?
824 */
f5691f9c 825 nonce->user = user;
2d70df72 826}
827
828/* setup the necessary info to log the username */
a33a428a
AJ
829static AuthUserRequest::Pointer
830authDigestLogUsername(char *username, AuthUserRequest::Pointer auth_user_request)
2d70df72 831{
f5691f9c 832 assert(auth_user_request != NULL);
2d70df72 833
834 /* log the username */
bf8fe701 835 debugs(29, 9, "authDigestLogUsername: Creating new user for logging '" << username << "'");
56a49fda 836 AuthUser::Pointer digest_user = new DigestUser(static_cast<AuthDigestConfig*>(AuthConfig::Find("digest")));
2d70df72 837 /* save the credentials */
f5691f9c 838 digest_user->username(username);
2d70df72 839 /* set the auth_user type */
f5691f9c 840 digest_user->auth_type = AUTH_BROKEN;
2d70df72 841 /* link the request to the user */
f5691f9c 842 auth_user_request->user(digest_user);
f5691f9c 843 return auth_user_request;
2d70df72 844}
845
846/*
847 * Decode a Digest [Proxy-]Auth string, placing the results in the passed
848 * Auth_user structure.
849 */
a33a428a 850AuthUserRequest::Pointer
f5691f9c 851AuthDigestConfig::decode(char const *proxy_auth)
2d70df72 852{
2d70df72 853 const char *item;
854 const char *p;
855 const char *pos = NULL;
856 char *username = NULL;
857 digest_nonce_h *nonce;
858 int ilen;
2d70df72 859
bf8fe701 860 debugs(29, 9, "authenticateDigestDecodeAuth: beginning");
2d70df72 861
f5691f9c 862 AuthDigestUserRequest *digest_request = new AuthDigestUserRequest();
2d70df72 863
864 /* trim DIGEST from string */
62e76326 865
ba53f4b8 866 while (xisgraph(*proxy_auth))
62e76326 867 proxy_auth++;
2d70df72 868
869 /* Trim leading whitespace before decoding */
870 while (xisspace(*proxy_auth))
62e76326 871 proxy_auth++;
2d70df72 872
30abd221 873 String temp(proxy_auth);
62e76326 874
2d70df72 875 while (strListGetItem(&temp, ',', &item, &ilen, &pos)) {
df604ac0 876 /* isolate directive name & value */
6d97f5f1 877 size_t nlen;
a0133f10 878 size_t vlen;
6d97f5f1 879 if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
9abd1514 880 nlen = p++ - item;
a0133f10 881 vlen = ilen - (p - item);
df604ac0 882 } else {
6d97f5f1 883 nlen = ilen;
a0133f10
A
884 vlen = 0;
885 }
9abd1514 886
a0133f10 887 /* parse value. auth-param = token "=" ( token | quoted-string ) */
df604ac0 888 String value;
a0133f10
A
889 if (vlen > 0) {
890 if (*p == '"') {
891 if (!httpHeaderParseQuotedString(p, &value)) {
892 debugs(29, 9, "authDigestDecodeAuth: Failed to parse attribute '" << item << "' in '" << temp << "'");
893 continue;
894 }
895 } else {
896 value.limitInit(p, vlen);
897 }
898 } else {
23a26549 899 debugs(29, 9, "authDigestDecodeAuth: Failed to parse attribute '" << item << "' in '" << temp << "'");
6d97f5f1
A
900 continue;
901 }
9abd1514 902
6d97f5f1
A
903 /* find type */
904 http_digest_attr_type type = (http_digest_attr_type)httpHeaderIdByName(item, nlen, DigestFieldsInfo, DIGEST_ENUM_END);
9abd1514 905
6d97f5f1
A
906 switch (type) {
907 case DIGEST_USERNAME:
bbe0ed86 908 safe_free(username);
9abd1514 909 username = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 910 debugs(29, 9, "authDigestDecodeAuth: Found Username '" << username << "'");
6d97f5f1 911 break;
62e76326 912
6d97f5f1 913 case DIGEST_REALM:
bbe0ed86 914 safe_free(digest_request->realm);
9abd1514 915 digest_request->realm = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 916 debugs(29, 9, "authDigestDecodeAuth: Found realm '" << digest_request->realm << "'");
6d97f5f1 917 break;
62e76326 918
6d97f5f1 919 case DIGEST_QOP:
bbe0ed86 920 safe_free(digest_request->qop);
9abd1514 921 digest_request->qop = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 922 debugs(29, 9, "authDigestDecodeAuth: Found qop '" << digest_request->qop << "'");
6d97f5f1 923 break;
62e76326 924
6d97f5f1 925 case DIGEST_ALGORITHM:
bbe0ed86 926 safe_free(digest_request->algorithm);
9abd1514 927 digest_request->algorithm = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 928 debugs(29, 9, "authDigestDecodeAuth: Found algorithm '" << digest_request->algorithm << "'");
6d97f5f1 929 break;
62e76326 930
6d97f5f1 931 case DIGEST_URI:
bbe0ed86 932 safe_free(digest_request->uri);
9abd1514 933 digest_request->uri = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 934 debugs(29, 9, "authDigestDecodeAuth: Found uri '" << digest_request->uri << "'");
6d97f5f1 935 break;
62e76326 936
6d97f5f1 937 case DIGEST_NONCE:
bbe0ed86 938 safe_free(digest_request->nonceb64);
9abd1514 939 digest_request->nonceb64 = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 940 debugs(29, 9, "authDigestDecodeAuth: Found nonce '" << digest_request->nonceb64 << "'");
6d97f5f1 941 break;
62e76326 942
6d97f5f1
A
943 case DIGEST_NC:
944 if (value.size() != 8) {
945 debugs(29, 9, "authDigestDecodeAuth: Invalid nc '" << value << "' in '" << temp << "'");
946 }
9abd1514 947 xstrncpy(digest_request->nc, value.rawBuf(), value.size() + 1);
bf8fe701 948 debugs(29, 9, "authDigestDecodeAuth: Found noncecount '" << digest_request->nc << "'");
6d97f5f1 949 break;
62e76326 950
6d97f5f1 951 case DIGEST_CNONCE:
bbe0ed86 952 safe_free(digest_request->cnonce);
9abd1514 953 digest_request->cnonce = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 954 debugs(29, 9, "authDigestDecodeAuth: Found cnonce '" << digest_request->cnonce << "'");
6d97f5f1 955 break;
62e76326 956
6d97f5f1 957 case DIGEST_RESPONSE:
bbe0ed86 958 safe_free(digest_request->response);
9abd1514 959 digest_request->response = xstrndup(value.rawBuf(), value.size() + 1);
bf8fe701 960 debugs(29, 9, "authDigestDecodeAuth: Found response '" << digest_request->response << "'");
6d97f5f1 961 break;
9abd1514 962
6d97f5f1 963 default:
59a98343 964 debugs(29, 3, "authDigestDecodeAuth: Unknown attribute '" << item << "' in '" << temp << "'");
0e134176 965 break;
62e76326 966 }
2d70df72 967 }
62e76326 968
30abd221 969 temp.clean();
2d70df72 970
971
972 /* now we validate the data given to us */
973
74830fc8 974 /*
975 * TODO: on invalid parameters we should return 400, not 407.
976 * Find some clean way of doing this. perhaps return a valid
977 * struct, and set the direction to clientwards combined with
978 * a change to the clientwards handling code (ie let the
979 * clientwards call set the error type (but limited to known
980 * correct values - 400/401/407
981 */
2d70df72 982
59a98343 983 /* 2069 requirements */
62e76326 984
59a98343
HN
985 /* do we have a username ? */
986 if (!username || username[0] == '\0') {
987 debugs(29, 2, "authenticateDigestDecode: Empty or not present username");
9f709824 988 return authDigestLogUsername(username, digest_request);
2d70df72 989 }
62e76326 990
920d1c9d
HN
991 /* Sanity check of the username.
992 * " can not be allowed in usernames until * the digest helper protocol
993 * have been redone
994 */
995 if (strchr(username, '"')) {
996 debugs(29, 2, "authenticateDigestDecode: Unacceptable username '" << username << "'");
997 return authDigestLogUsername(username, digest_request);
2d70df72 998 }
62e76326 999
59a98343
HN
1000 /* do we have a realm ? */
1001 if (!digest_request->realm || digest_request->realm[0] == '\0') {
1002 debugs(29, 2, "authenticateDigestDecode: Empty or not present realm");
9f709824 1003 return authDigestLogUsername(username, digest_request);
2d70df72 1004 }
62e76326 1005
59a98343
HN
1006 /* and a nonce? */
1007 if (!digest_request->nonceb64 || digest_request->nonceb64[0] == '\0') {
1008 debugs(29, 2, "authenticateDigestDecode: Empty or not present nonce");
9f709824 1009 return authDigestLogUsername(username, digest_request);
2d70df72 1010 }
62e76326 1011
74830fc8 1012 /* we can't check the URI just yet. We'll check it in the
6649f955 1013 * authenticate phase, but needs to be given */
59a98343
HN
1014 if (!digest_request->uri || digest_request->uri[0] == '\0') {
1015 debugs(29, 2, "authenticateDigestDecode: Missing URI field");
6649f955
HN
1016 return authDigestLogUsername(username, digest_request);
1017 }
2d70df72 1018
1019 /* is the response the correct length? */
2d70df72 1020 if (!digest_request->response || strlen(digest_request->response) != 32) {
59a98343 1021 debugs(29, 2, "authenticateDigestDecode: Response length invalid");
9f709824 1022 return authDigestLogUsername(username, digest_request);
2d70df72 1023 }
62e76326 1024
59a98343
HN
1025 /* check the algorithm is present and supported */
1026 if (!digest_request->algorithm)
1027 digest_request->algorithm = xstrndup("MD5", 4);
1028 else if (strcmp(digest_request->algorithm, "MD5")
1029 && strcmp(digest_request->algorithm, "MD5-sess")) {
d6b7a3c4 1030 debugs(29, 2, "authenticateDigestDecode: invalid algorithm specified!");
9f709824 1031 return authDigestLogUsername(username, digest_request);
2d70df72 1032 }
62e76326 1033
59a98343
HN
1034 /* 2617 requirements, indicated by qop */
1035 if (digest_request->qop) {
1036
6d97f5f1
A
1037 /* check the qop is what we expected. */
1038 if (strcmp(digest_request->qop, QOP_AUTH) != 0) {
1039 /* we received a qop option we didn't send */
1040 debugs(29, 2, "authenticateDigestDecode: Invalid qop option received");
1041 return authDigestLogUsername(username, digest_request);
1042 }
1043
1044 /* check cnonce */
1045 if (!digest_request->cnonce || digest_request->cnonce[0] == '\0') {
1046 debugs(29, 2, "authenticateDigestDecode: Missing cnonce field");
1047 return authDigestLogUsername(username, digest_request);
1048 }
1049
1050 /* check nc */
1051 if (strlen(digest_request->nc) != 8 || strspn(digest_request->nc, "0123456789abcdefABCDEF") != 8) {
1052 debugs(29, 2, "authenticateDigestDecode: invalid nonce count");
1053 return authDigestLogUsername(username, digest_request);
1054 }
59a98343 1055 } else {
6d97f5f1
A
1056 /* cnonce and nc both require qop */
1057 if (digest_request->cnonce || digest_request->nc) {
1058 debugs(29, 2, "authenticateDigestDecode: missing qop!");
1059 return authDigestLogUsername(username, digest_request);
1060 }
2d70df72 1061 }
62e76326 1062
59a98343
HN
1063 /** below nonce state dependent **/
1064
1065 /* now the nonce */
1066 nonce = authenticateDigestNonceFindNonce(digest_request->nonceb64);
1067 if (!nonce) {
1068 /* we couldn't find a matching nonce! */
1069 debugs(29, 2, "authenticateDigestDecode: Unexpected or invalid nonce received");
d232141d 1070 digest_request->user()->credentials(AuthUser::Failed);
9f709824 1071 return authDigestLogUsername(username, digest_request);
2d70df72 1072 }
62e76326 1073
59a98343
HN
1074 digest_request->nonce = nonce;
1075 authDigestNonceLink(nonce);
1076
1077 /* check that we're not being hacked / the username hasn't changed */
1078 if (nonce->user && strcmp(username, nonce->user->username())) {
1079 debugs(29, 2, "authenticateDigestDecode: Username for the nonce does not equal the username for the request");
9f709824 1080 return authDigestLogUsername(username, digest_request);
2d70df72 1081 }
62e76326 1082
2d70df72 1083 /* the method we'll check at the authenticate step as well */
1084
1085
1086 /* we don't send or parse opaques. Ok so we're flexable ... */
1087
1088 /* find the user */
56a49fda 1089 DigestUser *digest_user;
f5691f9c 1090
56a49fda 1091 AuthUser::Pointer auth_user;
2d70df72 1092
1093 if ((auth_user = authDigestUserFindUsername(username)) == NULL) {
62e76326 1094 /* the user doesn't exist in the username cache yet */
bf8fe701 1095 debugs(29, 9, "authDigestDecodeAuth: Creating new digest user '" << username << "'");
5817ee13 1096 digest_user = new DigestUser(this);
f5691f9c 1097 /* auth_user is a parent */
1098 auth_user = digest_user;
62e76326 1099 /* save the username */
f5691f9c 1100 digest_user->username(username);
62e76326 1101 /* set the user type */
f5691f9c 1102 digest_user->auth_type = AUTH_DIGEST;
62e76326 1103 /* this auth_user struct is the one to get added to the
1104 * username cache */
1105 /* store user in hash's */
f5691f9c 1106 digest_user->addToNameCache();
df80d445 1107
62e76326 1108 /*
1109 * Add the digest to the user so we can tell if a hacking
1110 * or spoofing attack is taking place. We do this by assuming
1111 * the user agent won't change user name without warning.
1112 */
f5691f9c 1113 authDigestUserLinkNonce(digest_user, nonce);
2d70df72 1114 } else {
bf8fe701 1115 debugs(29, 9, "authDigestDecodeAuth: Found user '" << username << "' in the user cache as '" << auth_user << "'");
56a49fda 1116 digest_user = static_cast<DigestUser *>(auth_user.getRaw());
62e76326 1117 xfree(username);
2d70df72 1118 }
62e76326 1119
2d70df72 1120 /*link the request and the user */
f5691f9c 1121 assert(digest_request != NULL);
82b045dc 1122
f5691f9c 1123 digest_request->user(digest_user);
bf8fe701 1124 debugs(29, 9, "username = '" << digest_user->username() << "'\nrealm = '" <<
1125 digest_request->realm << "'\nqop = '" << digest_request->qop <<
1126 "'\nalgorithm = '" << digest_request->algorithm << "'\nuri = '" <<
1127 digest_request->uri << "'\nnonce = '" << digest_request->nonceb64 <<
1128 "'\nnc = '" << digest_request->nc << "'\ncnonce = '" <<
1129 digest_request->cnonce << "'\nresponse = '" <<
1130 digest_request->response << "'\ndigestnonce = '" << nonce << "'");
2d70df72 1131
f5691f9c 1132 return digest_request;
2d70df72 1133}
1134
56a49fda 1135DigestUser::DigestUser(AuthConfig *aConfig) : AuthUser(aConfig), HA1created (0)
82b045dc 1136{}