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