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