]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/basic/auth_basic.cc
Remove duplicate basic auth authenticatestatedata
[thirdparty/squid.git] / src / auth / basic / auth_basic.cc
CommitLineData
94439e4e 1/*
262a0e14 2 * $Id$
94439e4e 3 *
4 * DEBUG: section 29 Authenticator
5 * AUTHOR: Duane Wessels
6 *
2b6662ba 7 * SQUID Web Proxy Cache http://www.squid-cache.org/
94439e4e 8 * ----------------------------------------------------------
9 *
2b6662ba 10 * Squid is the result of efforts by numerous individuals from
11 * the Internet community; see the CONTRIBUTORS file for full
12 * details. Many organizations have provided support for Squid's
13 * development; see the SPONSORS file for full details. Squid is
14 * Copyrighted (C) 2001 by the Regents of the University of
15 * California; see the COPYRIGHT file for full details. Squid
16 * incorporates software developed and/or copyrighted by other
17 * sources; see the CREDITS file for full details.
94439e4e 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 *
94439e4e 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 *
94439e4e 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"
5817ee13 41#include "auth/basic/auth_basic.h"
a623c828 42#include "auth/basic/basicScheme.h"
928f3421 43#include "auth/basic/basicUserRequest.h"
3ad63615 44#include "auth/Gadgets.h"
a623c828 45#include "auth/State.h"
62ee09ca 46#include "CacheManager.h"
e6ccf245 47#include "Store.h"
924f73bc 48#include "HttpReply.h"
1fa9b1a7 49#include "rfc1738.h"
d295d770 50#include "wordlist.h"
985c86bc 51#include "SquidTime.h"
94439e4e 52
94439e4e 53/* Basic Scheme */
94439e4e 54static HLPCB authenticateBasicHandleReply;
94439e4e 55static AUTHSSTATS authenticateBasicStats;
94439e4e 56
57static helper *basicauthenticators = NULL;
58
94439e4e 59static int authbasic_initialised = 0;
94439e4e 60
2d72d4fd 61
94439e4e 62/*
63 *
2d72d4fd 64 * Public Functions
94439e4e 65 *
66 */
67
2d72d4fd 68/* internal functions */
69
f5691f9c 70bool
71AuthBasicConfig::active() const
2d70df72 72{
f5691f9c 73 return authbasic_initialised == 1;
2d70df72 74}
75
f5691f9c 76bool
77AuthBasicConfig::configured() const
94439e4e 78{
48d54e4d 79 if ((authenticate != NULL) && (authenticateChildren.n_max != 0) &&
f5691f9c 80 (basicAuthRealm != NULL)) {
4c73ba5f 81 debugs(29, 9, HERE << "returning configured");
f5691f9c 82 return true;
2d70df72 83 }
62e76326 84
4c73ba5f 85 debugs(29, 9, HERE << "returning unconfigured");
f5691f9c 86 return false;
94439e4e 87}
88
f5691f9c 89const char *
90AuthBasicConfig::type() const
94439e4e 91{
5817ee13 92 return basicScheme::GetInstance()->type();
f5691f9c 93}
62e76326 94
f5691f9c 95
96bool
97BasicUser::authenticated() const
94439e4e 98{
5817ee13 99 if ((flags.credentials_ok == 1) && (credentials_checkedtime + static_cast<AuthBasicConfig*>(config)->credentialsTTL > squid_curtime))
f5691f9c 100 return true;
101
bf8fe701 102 debugs(29, 4, "User not authenticated or credentials need rechecking.");
f5691f9c 103
104 return false;
94439e4e 105}
62e76326 106
94439e4e 107void
a33a428a 108AuthBasicConfig::fixHeader(AuthUserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
94439e4e 109{
f5691f9c 110 if (authenticate) {
18ec8500
FC
111 debugs(29, 9, HERE << "Sending type:" << hdrType << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
112 httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"%s\"", basicAuthRealm);
94439e4e 113 }
114}
115
5817ee13 116/** shutdown the auth helpers and free any allocated configuration details */
94439e4e 117void
f5691f9c 118AuthBasicConfig::done()
94439e4e 119{
5817ee13
AJ
120 authbasic_initialised = 0;
121
122 if (basicauthenticators) {
123 helperShutdown(basicauthenticators);
5817ee13
AJ
124 }
125
ed3ef6a8
AJ
126 delete basicauthenticators;
127 basicauthenticators = NULL;
128
f5691f9c 129 if (authenticate)
130 wordlistDestroy(&authenticate);
62e76326 131
f5691f9c 132 if (basicAuthRealm)
133 safe_free(basicAuthRealm);
94439e4e 134}
135
f5691f9c 136BasicUser::~BasicUser()
94439e4e 137{
4c73ba5f
AJ
138 safe_free(passwd);
139 safe_free(cleartext);
94439e4e 140}
141
142static void
143authenticateBasicHandleReply(void *data, char *reply)
144{
a623c828 145 authenticateStateData *r = static_cast<authenticateStateData *>(data);
e6ccf245 146 BasicAuthQueueNode *tmpnode;
94439e4e 147 char *t = NULL;
fa80a8ef 148 void *cbdata;
4c73ba5f 149 debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
62e76326 150
94439e4e 151 if (reply) {
62e76326 152 if ((t = strchr(reply, ' ')))
0a0c70cd 153 *t++ = '\0';
62e76326 154
155 if (*reply == '\0')
156 reply = NULL;
94439e4e 157 }
62e76326 158
94439e4e 159 assert(r->auth_user_request != NULL);
f5691f9c 160 assert(r->auth_user_request->user()->auth_type == AUTH_BASIC);
161 basic_data *basic_auth = dynamic_cast<basic_data *>(r->auth_user_request->user());
62e76326 162
2948b229 163 assert(basic_auth != NULL);
164
94439e4e 165 if (reply && (strncasecmp(reply, "OK", 2) == 0))
62e76326 166 basic_auth->flags.credentials_ok = 1;
0a0c70cd 167 else {
62e76326 168 basic_auth->flags.credentials_ok = 3;
169
0a0c70cd 170 if (t && *t)
171 r->auth_user_request->setDenyMessage(t);
172 }
173
94439e4e 174 basic_auth->credentials_checkedtime = squid_curtime;
62e76326 175
fa80a8ef 176 if (cbdataReferenceValidDone(r->data, &cbdata))
62e76326 177 r->handler(cbdata, NULL);
178
fa80a8ef 179 cbdataReferenceDone(r->data);
62e76326 180
f2afa96a 181 while (basic_auth->auth_queue) {
62e76326 182 tmpnode = basic_auth->auth_queue->next;
183
184 if (cbdataReferenceValidDone(basic_auth->auth_queue->data, &cbdata))
185 basic_auth->auth_queue->handler(cbdata, NULL);
186
187 xfree(basic_auth->auth_queue);
188
189 basic_auth->auth_queue = tmpnode;
94439e4e 190 }
62e76326 191
94439e4e 192 authenticateStateFree(r);
193}
194
f5691f9c 195void
196AuthBasicConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
94439e4e 197{
f5691f9c 198 wordlist *list = authenticate;
94439e4e 199 storeAppendPrintf(entry, "%s %s", name, "basic");
62e76326 200
94439e4e 201 while (list != NULL) {
62e76326 202 storeAppendPrintf(entry, " %s", list->key);
203 list = list->next;
94439e4e 204 }
62e76326 205
07eca7e0 206 storeAppendPrintf(entry, "\n");
207
f5691f9c 208 storeAppendPrintf(entry, "%s basic realm %s\n", name, basicAuthRealm);
404cfda1 209 storeAppendPrintf(entry, "%s basic children %d startup=%d idle=%d concurrency=%d\n", name, authenticateChildren.n_max, authenticateChildren.n_startup, authenticateChildren.n_idle, authenticateChildren.concurrency);
f5691f9c 210 storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL);
64658378 211 storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
94439e4e 212}
213
5817ee13 214AuthBasicConfig::AuthBasicConfig() :
ed3ef6a8 215 authenticateChildren(20,0,1,1),
5817ee13
AJ
216 authenticate(NULL),
217 credentialsTTL( 2*60*60 ),
218 casesensitive(0),
219 utf8(0)
f5691f9c 220{
75126633 221 basicAuthRealm = xstrdup("Squid proxy-caching web server");
f5691f9c 222}
62e76326 223
3845e4c8 224AuthBasicConfig::~AuthBasicConfig()
225{
acde4327 226 safe_free(basicAuthRealm);
3845e4c8 227}
228
f5691f9c 229void
230AuthBasicConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
231{
94439e4e 232 if (strcasecmp(param_str, "program") == 0) {
f5691f9c 233 if (authenticate)
234 wordlistDestroy(&authenticate);
62e76326 235
f5691f9c 236 parse_wordlist(&authenticate);
62e76326 237
42900318 238 requirePathnameExists("auth_param basic program", authenticate->key);
94439e4e 239 } else if (strcasecmp(param_str, "children") == 0) {
48d54e4d 240 authenticateChildren.parseConfig();
94439e4e 241 } else if (strcasecmp(param_str, "realm") == 0) {
f5691f9c 242 parse_eol(&basicAuthRealm);
94439e4e 243 } else if (strcasecmp(param_str, "credentialsttl") == 0) {
f5691f9c 244 parse_time_t(&credentialsTTL);
64658378 245 } else if (strcasecmp(param_str, "casesensitive") == 0) {
246 parse_onoff(&casesensitive);
f741d2f6
HN
247 } else if (strcasecmp(param_str, "utf8") == 0) {
248 parse_onoff(&utf8);
94439e4e 249 } else {
4c73ba5f 250 debugs(29, DBG_CRITICAL, HERE << "unrecognised basic auth scheme parameter '" << param_str << "'");
94439e4e 251 }
252}
253
254static void
255authenticateBasicStats(StoreEntry * sentry)
256{
9522b380 257 helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
94439e4e 258}
259
e1f7507e 260static AuthUser *
94439e4e 261authBasicAuthUserFindUsername(const char *username)
262{
e6ccf245 263 AuthUserHashPointer *usernamehash;
e1f7507e 264 debugs(29, 9, HERE << "Looking for user '" << username << "'");
62e76326 265
e6ccf245 266 if (username && (usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, username)))) {
62e76326 267 while (usernamehash) {
f5691f9c 268 if ((usernamehash->user()->auth_type == AUTH_BASIC) &&
62e76326 269 !strcmp(username, (char const *)usernamehash->key))
f5691f9c 270 return usernamehash->user();
62e76326 271
272 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
273 }
94439e4e 274 }
62e76326 275
94439e4e 276 return NULL;
277}
278
f5691f9c 279void
280BasicUser::deleteSelf() const
281{
282 delete this;
283}
94439e4e 284
18ec8500 285BasicUser::BasicUser(AuthConfig *aConfig) : AuthUser (aConfig) , passwd (NULL), credentials_checkedtime(0), auth_queue(NULL), cleartext (NULL), currentRequest (NULL), httpAuthHeader (NULL)
f5691f9c 286{
287 flags.credentials_ok = 0;
288}
62e76326 289
35b3bc89 290bool
f5691f9c 291BasicUser::decodeCleartext()
292{
acde4327
AJ
293 char *sent_auth = NULL;
294
94439e4e 295 /* username and password */
f5691f9c 296 sent_auth = xstrdup(httpAuthHeader);
acde4327 297
94439e4e 298 /* Trim trailing \n before decoding */
299 strtok(sent_auth, "\n");
62e76326 300
94439e4e 301 cleartext = uudecode(sent_auth);
62e76326 302
acde4327
AJ
303 safe_free(sent_auth);
304
305 if (!cleartext)
306 return false;
62e76326 307
94439e4e 308 /*
309 * Don't allow NL or CR in the credentials.
310 * Oezguer Kesim <oec@codeblau.de>
311 */
4c73ba5f 312 debugs(29, 9, HERE << "'" << cleartext << "'");
62e76326 313
36a04c15 314 if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
4c73ba5f 315 debugs(29, 1, HERE << "bad characters in authorization header '" << httpAuthHeader << "'");
147c7544 316 safe_free(cleartext);
317 return false;
36a04c15 318 }
35b3bc89 319 return true;
320}
36a04c15 321
35b3bc89 322void
323BasicUser::extractUsername()
324{
acde4327 325 char * seperator = strchr(cleartext, ':');
62e76326 326
acde4327
AJ
327 if (seperator == NULL) {
328 username(cleartext);
329 } else {
330 /* terminate the username */
331 *seperator = '\0';
332
333 username(cleartext);
64658378 334
acde4327
AJ
335 /* replace the colon so we can find the password */
336 *seperator = ':';
337 }
411c6ea3 338
5817ee13 339 if (!static_cast<AuthBasicConfig*>(config)->casesensitive)
64658378 340 Tolower((char *)username());
f5691f9c 341}
62e76326 342
f5691f9c 343void
344BasicUser::extractPassword()
345{
acde4327 346 passwd = strchr(cleartext, ':');
62e76326 347
acde4327 348 if (passwd == NULL) {
4c73ba5f 349 debugs(29, 4, HERE << "no password in proxy authorization header '" << httpAuthHeader << "'");
f5691f9c 350 passwd = NULL;
4c73ba5f 351 currentRequest->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
acde4327
AJ
352 } else {
353 ++passwd;
354 if (*passwd == '\0') {
4c73ba5f 355 debugs(29, 4, HERE << "Disallowing empty password,user is '" << username() << "'");
acde4327 356 passwd = NULL;
4c73ba5f 357 currentRequest->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
acde4327
AJ
358 } else {
359 passwd = xstrndup(passwd, USER_IDENT_SZ);
360 }
94439e4e 361 }
f5691f9c 362}
94439e4e 363
f5691f9c 364void
a33a428a 365BasicUser::decode(char const *proxy_auth, AuthUserRequest::Pointer auth_user_request)
f5691f9c 366{
367 currentRequest = auth_user_request;
368 httpAuthHeader = proxy_auth;
a33a428a 369 if (decodeCleartext()) {
26ac0430
AJ
370 extractUsername();
371 extractPassword();
35b3bc89 372 }
f5691f9c 373 currentRequest = NULL;
374 httpAuthHeader = NULL;
375}
376
377bool
378BasicUser::valid() const
379{
147c7544 380 if (username() == NULL)
26ac0430 381 return false;
147c7544 382 if (passwd == NULL)
26ac0430 383 return false;
147c7544 384 return true;
f5691f9c 385}
94439e4e 386
f5691f9c 387void
a33a428a 388BasicUser::makeLoggingInstance(AuthUserRequest::Pointer auth_user_request)
f5691f9c 389{
390 if (username()) {
391 /* log the username */
4c73ba5f 392 debugs(29, 9, HERE << "Creating new user for logging '" << username() << "'");
62e76326 393 /* new scheme data */
5817ee13 394 BasicUser *basic_auth = new BasicUser(config);
f5691f9c 395 auth_user_request->user(basic_auth);
62e76326 396 /* save the credentials */
f5691f9c 397 basic_auth->username(username());
398 username(NULL);
62e76326 399 /* set the auth_user type */
f5691f9c 400 basic_auth->auth_type = AUTH_BROKEN;
401 /* link the request to the user */
402 basic_auth->addRequest(auth_user_request);
403 }
404}
405
406AuthUser *
407BasicUser::makeCachedFrom()
408{
409 /* the user doesn't exist in the username cache yet */
4c73ba5f 410 debugs(29, 9, HERE << "Creating new user '" << username() << "'");
5817ee13 411 BasicUser *basic_user = new BasicUser(config);
f5691f9c 412 /* save the credentials */
413 basic_user->username(username());
414 username(NULL);
415 basic_user->passwd = passwd;
416 passwd = NULL;
417 /* set the auth_user type */
418 basic_user->auth_type = AUTH_BASIC;
419 /* current time for timeouts */
420 basic_user->expiretime = current_time.tv_sec;
421
422 /* this basic_user struct is the 'lucky one' to get added to the username cache */
423 /* the requests after this link to the basic_user */
424 /* store user in hash */
425 basic_user->addToNameCache();
426 return basic_user;
427}
428
429void
430BasicUser::updateCached(BasicUser *from)
431{
4c73ba5f 432 debugs(29, 9, HERE << "Found user '" << from->username() << "' in the user cache as '" << this << "'");
f5691f9c 433
434 if (strcmp(from->passwd, passwd)) {
4c73ba5f 435 debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked");
f5691f9c 436 flags.credentials_ok = 0;
437 xfree(passwd);
438 passwd = from->passwd;
439 from->passwd = NULL;
440 }
441
442 if (flags.credentials_ok == 3) {
4c73ba5f 443 debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked");
f5691f9c 444 flags.credentials_ok = 0;
445 }
446}
447
4c73ba5f 448/**
f5691f9c 449 * Decode a Basic [Proxy-]Auth string, linking the passed
450 * auth_user_request structure to any existing user structure or creating one
26ac0430
AJ
451 * if needed. Note that just returning will be treated as
452 * "cannot decode credentials". Use the message field to return a
f5691f9c 453 * descriptive message to the user.
454 */
a33a428a 455AuthUserRequest::Pointer
f5691f9c 456AuthBasicConfig::decode(char const *proxy_auth)
457{
928f3421 458 AuthUserRequest::Pointer auth_user_request = dynamic_cast<AuthUserRequest*>(new AuthBasicUserRequest);
f5691f9c 459 /* decode the username */
460 /* trim BASIC from string */
461
ba53f4b8 462 while (xisgraph(*proxy_auth))
f5691f9c 463 proxy_auth++;
464
5817ee13 465 BasicUser *basic_auth, local_basic(this);
f5691f9c 466
467 /* Trim leading whitespace before decoding */
468 while (xisspace(*proxy_auth))
469 proxy_auth++;
470
471 local_basic.decode(proxy_auth, auth_user_request);
472
473 if (!local_basic.valid()) {
474 local_basic.makeLoggingInstance(auth_user_request);
475 return auth_user_request;
94439e4e 476 }
62e76326 477
f5691f9c 478 /* now lookup and see if we have a matching auth_user structure in
479 * memory. */
62e76326 480
e1f7507e 481 AuthUser *auth_user;
62e76326 482
f5691f9c 483 if ((auth_user = authBasicAuthUserFindUsername(local_basic.username())) == NULL) {
484 auth_user = local_basic.makeCachedFrom();
485 basic_auth = dynamic_cast<BasicUser *>(auth_user);
486 assert (basic_auth);
487 } else {
488 basic_auth = dynamic_cast<BasicUser *>(auth_user);
489 assert (basic_auth);
490 basic_auth->updateCached (&local_basic);
491 }
62e76326 492
f5691f9c 493 /* link the request to the in-cache user */
494 auth_user_request->user(basic_auth);
62e76326 495
f5691f9c 496 basic_auth->addRequest(auth_user_request);
497
498 return auth_user_request;
94439e4e 499}
500
4c73ba5f 501/** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
94439e4e 502 * config file */
f5691f9c 503void
5817ee13 504AuthBasicConfig::init(AuthConfig * schemeCfg)
94439e4e 505{
f5691f9c 506 if (authenticate) {
62e76326 507 authbasic_initialised = 1;
508
509 if (basicauthenticators == NULL)
48d54e4d 510 basicauthenticators = new helper("basicauthenticator");
62e76326 511
f5691f9c 512 basicauthenticators->cmdline = authenticate;
62e76326 513
48d54e4d 514 basicauthenticators->childs = authenticateChildren;
07eca7e0 515
62e76326 516 basicauthenticators->ipc_type = IPC_STREAM;
517
518 helperOpenServers(basicauthenticators);
519
a623c828 520 CBDATA_INIT_TYPE(authenticateStateData);
94439e4e 521 }
522}
523
62ee09ca 524void
15fab853 525AuthBasicConfig::registerWithCacheManager(void)
62ee09ca 526{
15fab853 527 CacheManager::GetInstance()->
26ac0430
AJ
528 registerAction("basicauthenticator",
529 "Basic User Authenticator Stats",
530 authenticateBasicStats, 0, 1);
62ee09ca 531}
532
f5691f9c 533void
a33a428a 534BasicUser::queueRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
f5691f9c 535{
536 BasicAuthQueueNode *node;
537 node = static_cast<BasicAuthQueueNode *>(xmalloc(sizeof(BasicAuthQueueNode)));
538 assert(node);
539 /* save the details */
540 node->next = auth_queue;
541 auth_queue = node;
542 node->auth_user_request = auth_user_request;
543 node->handler = handler;
544 node->data = cbdataReference(data);
545}
546
f5691f9c 547void
a33a428a 548BasicUser::submitRequest(AuthUserRequest::Pointer auth_user_request, RH * handler, void *data)
f5691f9c 549{
928f3421 550 /* mark the user as having verification in progress */
f5691f9c 551 flags.credentials_ok = 2;
a623c828 552 authenticateStateData *r = NULL;
f5691f9c 553 char buf[8192];
554 char user[1024], pass[1024];
a623c828 555 r = cbdataAlloc(authenticateStateData);
f5691f9c 556 r->handler = handler;
557 r->data = cbdataReference(data);
558 r->auth_user_request = auth_user_request;
5817ee13 559 if (static_cast<AuthBasicConfig*>(config)->utf8) {
26ac0430
AJ
560 latin1_to_utf8(user, sizeof(user), username());
561 latin1_to_utf8(pass, sizeof(pass), passwd);
562 xstrncpy(user, rfc1738_escape(user), sizeof(user));
563 xstrncpy(pass, rfc1738_escape(pass), sizeof(pass));
f741d2f6 564 } else {
26ac0430
AJ
565 xstrncpy(user, rfc1738_escape(username()), sizeof(user));
566 xstrncpy(pass, rfc1738_escape(passwd), sizeof(pass));
f741d2f6 567 }
f5691f9c 568 snprintf(buf, sizeof(buf), "%s %s\n", user, pass);
569 helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
570}