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