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