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