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