]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/basic/auth_basic.cc
Cleanup: zap CVS Id tags
[thirdparty/squid.git] / src / auth / basic / auth_basic.cc
1 /*
2 * $Id$
3 *
4 * DEBUG: section 29 Authenticator
5 * AUTHOR: Duane Wessels
6 *
7 * SQUID Web Proxy Cache http://www.squid-cache.org/
8 * ----------------------------------------------------------
9 *
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.
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
32 *
33 */
34
35 /* The functions in this file handle authentication.
36 * They DO NOT perform access control or auditing.
37 * See acl.c for access control and client_side.c for auditing */
38
39
40 #include "squid.h"
41 #include "auth_basic.h"
42 #include "authenticate.h"
43 #include "CacheManager.h"
44 #include "Store.h"
45 #include "HttpReply.h"
46 #include "basicScheme.h"
47 #include "wordlist.h"
48 #include "SquidTime.h"
49
50 static void
51 authenticateStateFree(AuthenticateStateData * r)
52 {
53 cbdataFree(r);
54 }
55
56 /* Basic Scheme */
57
58 static HLPCB authenticateBasicHandleReply;
59 static AUTHSSTATS authenticateBasicStats;
60
61 static helper *basicauthenticators = NULL;
62
63 static AuthBasicConfig basicConfig;
64
65 static int authbasic_initialised = 0;
66
67
68 /*
69 *
70 * Public Functions
71 *
72 */
73
74 /* internal functions */
75
76 /* TODO: move to basicScheme.cc - after all per request and user functions are moved out */
77 void
78 basicScheme::done()
79 {
80 /* TODO: this should be a Config call. */
81
82 if (basicauthenticators)
83 helperShutdown(basicauthenticators);
84
85 authbasic_initialised = 0;
86
87 if (!shutting_down)
88 return;
89
90 if (basicauthenticators)
91 helperFree(basicauthenticators);
92
93 basicauthenticators = NULL;
94
95 /* XXX Reinstate auth shutdown for dynamic schemes? */
96 debugs(29, DBG_CRITICAL, HERE << "Basic authentication Shutdown.");
97 }
98
99 bool
100 AuthBasicConfig::active() const
101 {
102 return authbasic_initialised == 1;
103 }
104
105 bool
106 AuthBasicConfig::configured() const
107 {
108 if ((authenticate != NULL) && (authenticateChildren != 0) &&
109 (basicAuthRealm != NULL)) {
110 debugs(29, 9, HERE << "returning configured");
111 return true;
112 }
113
114 debugs(29, 9, HERE << "returning unconfigured");
115 return false;
116 }
117
118 const char *
119 AuthBasicConfig::type() const
120 {
121 return basicScheme::GetInstance().type();
122 }
123
124 AuthBasicUserRequest::AuthBasicUserRequest() : _theUser(NULL)
125 {}
126
127 AuthBasicUserRequest::~AuthBasicUserRequest()
128 {}
129
130
131 bool
132 BasicUser::authenticated() const
133 {
134 if ((flags.credentials_ok == 1) && (credentials_checkedtime + basicConfig.credentialsTTL > squid_curtime))
135 return true;
136
137 debugs(29, 4, "User not authenticated or credentials need rechecking.");
138
139 return false;
140 }
141
142 int
143 AuthBasicUserRequest::authenticated() const
144 {
145 BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user());
146 assert (basic_auth != NULL);
147
148 if (basic_auth->authenticated())
149 return 1;
150
151 return 0;
152 }
153
154 /* log a basic user in
155 */
156 void
157 AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
158 {
159 assert(user() != NULL);
160
161 basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
162
163 /* if the password is not ok, do an identity */
164
165 if (!basic_auth || basic_auth->flags.credentials_ok != 1)
166 return;
167
168 /* are we about to recheck the credentials externally? */
169 if ((basic_auth->credentials_checkedtime + basicConfig.credentialsTTL) <= squid_curtime) {
170 debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking");
171 return;
172 }
173
174 /* we have been through the external helper, and the credentials haven't expired */
175 debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated");
176
177 /* Decode now takes care of finding the AuthUser struct in the cache */
178 /* after external auth occurs anyway */
179 basic_auth->expiretime = current_time.tv_sec;
180
181 return;
182 }
183
184 int
185 AuthBasicUserRequest::module_direction()
186 {
187 /* null auth_user is checked for by authenticateDirection */
188 basic_data *basic_auth = dynamic_cast<BasicUser *>(user());
189 assert (basic_auth);
190
191 switch (basic_auth->flags.credentials_ok) {
192
193 case 0: /* not checked */
194 return -1;
195
196 case 1: /* checked & ok */
197
198 if (basic_auth->credentials_checkedtime + basicConfig.credentialsTTL <= squid_curtime)
199 return -1;
200
201 return 0;
202
203 case 2: /* paused while waiting for a username:password check on another request */
204 return -1;
205
206 case 3: /* authentication process failed. */
207 return 0;
208 }
209
210 return -2;
211 }
212
213 void
214 AuthBasicConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request)
215 {
216 if (authenticate) {
217 debugs(29, 9, HERE << "Sending type:" << type << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
218 httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", basicAuthRealm);
219 }
220 }
221
222 /* free any allocated configuration details */
223 void
224 AuthBasicConfig::done()
225 {
226 if (authenticate)
227 wordlistDestroy(&authenticate);
228
229 if (basicAuthRealm)
230 safe_free(basicAuthRealm);
231 }
232
233 BasicUser::~BasicUser()
234 {
235 safe_free(passwd);
236 safe_free(cleartext);
237 }
238
239 static void
240 authenticateBasicHandleReply(void *data, char *reply)
241 {
242 AuthenticateStateData *r = static_cast<AuthenticateStateData *>(data);
243 BasicAuthQueueNode *tmpnode;
244 char *t = NULL;
245 void *cbdata;
246 debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
247
248 if (reply) {
249 if ((t = strchr(reply, ' ')))
250 *t++ = '\0';
251
252 if (*reply == '\0')
253 reply = NULL;
254 }
255
256 assert(r->auth_user_request != NULL);
257 assert(r->auth_user_request->user()->auth_type == AUTH_BASIC);
258 basic_data *basic_auth = dynamic_cast<basic_data *>(r->auth_user_request->user());
259
260 assert(basic_auth != NULL);
261
262 if (reply && (strncasecmp(reply, "OK", 2) == 0))
263 basic_auth->flags.credentials_ok = 1;
264 else {
265 basic_auth->flags.credentials_ok = 3;
266
267 if (t && *t)
268 r->auth_user_request->setDenyMessage(t);
269 }
270
271 basic_auth->credentials_checkedtime = squid_curtime;
272
273 if (cbdataReferenceValidDone(r->data, &cbdata))
274 r->handler(cbdata, NULL);
275
276 cbdataReferenceDone(r->data);
277
278 while (basic_auth->auth_queue) {
279 tmpnode = basic_auth->auth_queue->next;
280
281 if (cbdataReferenceValidDone(basic_auth->auth_queue->data, &cbdata))
282 basic_auth->auth_queue->handler(cbdata, NULL);
283
284 xfree(basic_auth->auth_queue);
285
286 basic_auth->auth_queue = tmpnode;
287 }
288
289 authenticateStateFree(r);
290 }
291
292 void
293 AuthBasicConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme)
294 {
295 wordlist *list = authenticate;
296 storeAppendPrintf(entry, "%s %s", name, "basic");
297
298 while (list != NULL) {
299 storeAppendPrintf(entry, " %s", list->key);
300 list = list->next;
301 }
302
303 storeAppendPrintf(entry, "\n");
304
305 storeAppendPrintf(entry, "%s basic realm %s\n", name, basicAuthRealm);
306 storeAppendPrintf(entry, "%s basic children %d\n", name, authenticateChildren);
307 storeAppendPrintf(entry, "%s basic concurrency %d\n", name, authenticateConcurrency);
308 storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL);
309 storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
310 }
311
312 AuthBasicConfig::AuthBasicConfig()
313 {
314 /* TODO: move into initialisation list */
315 authenticateChildren = 5;
316 credentialsTTL = 2 * 60 * 60; /* two hours */
317 basicAuthRealm = xstrdup("Squid proxy-caching web server");
318 }
319
320 AuthBasicConfig::~AuthBasicConfig()
321 {
322 safe_free(basicAuthRealm);
323 }
324
325 void
326 AuthBasicConfig::parse(AuthConfig * scheme, int n_configured, char *param_str)
327 {
328 if (strcasecmp(param_str, "program") == 0) {
329 if (authenticate)
330 wordlistDestroy(&authenticate);
331
332 parse_wordlist(&authenticate);
333
334 requirePathnameExists("authparam basic program", authenticate->key);
335 } else if (strcasecmp(param_str, "children") == 0) {
336 parse_int(&authenticateChildren);
337 } else if (strcasecmp(param_str, "concurrency") == 0) {
338 parse_int(&authenticateConcurrency);
339 } else if (strcasecmp(param_str, "realm") == 0) {
340 parse_eol(&basicAuthRealm);
341 } else if (strcasecmp(param_str, "credentialsttl") == 0) {
342 parse_time_t(&credentialsTTL);
343 } else if (strcasecmp(param_str, "casesensitive") == 0) {
344 parse_onoff(&casesensitive);
345 } else if (strcasecmp(param_str, "utf8") == 0) {
346 parse_onoff(&utf8);
347 } else {
348 debugs(29, DBG_CRITICAL, HERE << "unrecognised basic auth scheme parameter '" << param_str << "'");
349 }
350 }
351
352 static void
353 authenticateBasicStats(StoreEntry * sentry)
354 {
355 helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
356 }
357
358 CBDATA_TYPE(AuthenticateStateData);
359
360 static AuthUser *
361 authBasicAuthUserFindUsername(const char *username)
362 {
363 AuthUserHashPointer *usernamehash;
364 debugs(29, 9, HERE << "Looking for user '" << username << "'");
365
366 if (username && (usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, username)))) {
367 while (usernamehash) {
368 if ((usernamehash->user()->auth_type == AUTH_BASIC) &&
369 !strcmp(username, (char const *)usernamehash->key))
370 return usernamehash->user();
371
372 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
373 }
374 }
375
376 return NULL;
377 }
378
379 void
380 BasicUser::deleteSelf() const
381 {
382 delete this;
383 }
384
385 BasicUser::BasicUser(AuthConfig *config) : AuthUser (config) , passwd (NULL), credentials_checkedtime(0), auth_queue(NULL), cleartext (NULL), currentRequest (NULL), httpAuthHeader (NULL)
386 {
387 flags.credentials_ok = 0;
388 }
389
390 bool
391 BasicUser::decodeCleartext()
392 {
393 char *sent_auth = NULL;
394
395 /* username and password */
396 sent_auth = xstrdup(httpAuthHeader);
397
398 /* Trim trailing \n before decoding */
399 strtok(sent_auth, "\n");
400
401 cleartext = uudecode(sent_auth);
402
403 safe_free(sent_auth);
404
405 if (!cleartext)
406 return false;
407
408 /*
409 * Don't allow NL or CR in the credentials.
410 * Oezguer Kesim <oec@codeblau.de>
411 */
412 debugs(29, 9, HERE << "'" << cleartext << "'");
413
414 if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
415 debugs(29, 1, HERE << "bad characters in authorization header '" << httpAuthHeader << "'");
416 safe_free(cleartext);
417 return false;
418 }
419 return true;
420 }
421
422 void
423 BasicUser::extractUsername()
424 {
425 char * seperator = strchr(cleartext, ':');
426
427 if (seperator == NULL) {
428 username(cleartext);
429 } else {
430 /* terminate the username */
431 *seperator = '\0';
432
433 username(cleartext);
434
435 /* replace the colon so we can find the password */
436 *seperator = ':';
437 }
438
439 if (!basicConfig.casesensitive)
440 Tolower((char *)username());
441 }
442
443 void
444 BasicUser::extractPassword()
445 {
446 passwd = strchr(cleartext, ':');
447
448 if (passwd == NULL) {
449 debugs(29, 4, HERE << "no password in proxy authorization header '" << httpAuthHeader << "'");
450 passwd = NULL;
451 currentRequest->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
452 } else {
453 ++passwd;
454 if (*passwd == '\0') {
455 debugs(29, 4, HERE << "Disallowing empty password,user is '" << username() << "'");
456 passwd = NULL;
457 currentRequest->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
458 } else {
459 passwd = xstrndup(passwd, USER_IDENT_SZ);
460 }
461 }
462 }
463
464 void
465 BasicUser::decode(char const *proxy_auth, AuthUserRequest *auth_user_request)
466 {
467 currentRequest = auth_user_request;
468 httpAuthHeader = proxy_auth;
469 if (decodeCleartext ()) {
470 extractUsername();
471 extractPassword();
472 }
473 currentRequest = NULL;
474 httpAuthHeader = NULL;
475 }
476
477 bool
478 BasicUser::valid() const
479 {
480 if (username() == NULL)
481 return false;
482 if (passwd == NULL)
483 return false;
484 return true;
485 }
486
487 void
488 BasicUser::makeLoggingInstance(AuthBasicUserRequest *auth_user_request)
489 {
490 if (username()) {
491 /* log the username */
492 debugs(29, 9, HERE << "Creating new user for logging '" << username() << "'");
493 /* new scheme data */
494 BasicUser *basic_auth = new BasicUser(& basicConfig);
495 auth_user_request->user(basic_auth);
496 /* save the credentials */
497 basic_auth->username(username());
498 username(NULL);
499 /* set the auth_user type */
500 basic_auth->auth_type = AUTH_BROKEN;
501 /* link the request to the user */
502 basic_auth->addRequest(auth_user_request);
503 }
504 }
505
506 AuthUser *
507 BasicUser::makeCachedFrom()
508 {
509 /* the user doesn't exist in the username cache yet */
510 debugs(29, 9, HERE << "Creating new user '" << username() << "'");
511 BasicUser *basic_user = new BasicUser(&basicConfig);
512 /* save the credentials */
513 basic_user->username(username());
514 username(NULL);
515 basic_user->passwd = passwd;
516 passwd = NULL;
517 /* set the auth_user type */
518 basic_user->auth_type = AUTH_BASIC;
519 /* current time for timeouts */
520 basic_user->expiretime = current_time.tv_sec;
521
522 /* this basic_user struct is the 'lucky one' to get added to the username cache */
523 /* the requests after this link to the basic_user */
524 /* store user in hash */
525 basic_user->addToNameCache();
526 return basic_user;
527 }
528
529 void
530 BasicUser::updateCached(BasicUser *from)
531 {
532 debugs(29, 9, HERE << "Found user '" << from->username() << "' in the user cache as '" << this << "'");
533
534 if (strcmp(from->passwd, passwd)) {
535 debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked");
536 flags.credentials_ok = 0;
537 xfree(passwd);
538 passwd = from->passwd;
539 from->passwd = NULL;
540 }
541
542 if (flags.credentials_ok == 3) {
543 debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked");
544 flags.credentials_ok = 0;
545 }
546 }
547
548 /**
549 * Decode a Basic [Proxy-]Auth string, linking the passed
550 * auth_user_request structure to any existing user structure or creating one
551 * if needed. Note that just returning will be treated as
552 * "cannot decode credentials". Use the message field to return a
553 * descriptive message to the user.
554 */
555 AuthUserRequest *
556 AuthBasicConfig::decode(char const *proxy_auth)
557 {
558 AuthBasicUserRequest *auth_user_request = new AuthBasicUserRequest();
559 /* decode the username */
560 /* trim BASIC from string */
561
562 while (xisgraph(*proxy_auth))
563 proxy_auth++;
564
565 BasicUser *basic_auth, local_basic(&basicConfig);
566
567 /* Trim leading whitespace before decoding */
568 while (xisspace(*proxy_auth))
569 proxy_auth++;
570
571 local_basic.decode(proxy_auth, auth_user_request);
572
573 if (!local_basic.valid()) {
574 local_basic.makeLoggingInstance(auth_user_request);
575 return auth_user_request;
576 }
577
578 /* now lookup and see if we have a matching auth_user structure in
579 * memory. */
580
581 AuthUser *auth_user;
582
583 if ((auth_user = authBasicAuthUserFindUsername(local_basic.username())) == NULL) {
584 auth_user = local_basic.makeCachedFrom();
585 basic_auth = dynamic_cast<BasicUser *>(auth_user);
586 assert (basic_auth);
587 } else {
588 basic_auth = dynamic_cast<BasicUser *>(auth_user);
589 assert (basic_auth);
590 basic_auth->updateCached (&local_basic);
591 }
592
593 /* link the request to the in-cache user */
594 auth_user_request->user(basic_auth);
595
596 basic_auth->addRequest(auth_user_request);
597
598 return auth_user_request;
599 }
600
601 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
602 * config file */
603 void
604 AuthBasicConfig::init(AuthConfig * scheme)
605 {
606 if (authenticate) {
607 authbasic_initialised = 1;
608
609 if (basicauthenticators == NULL)
610 basicauthenticators = helperCreate("basicauthenticator");
611
612 basicauthenticators->cmdline = authenticate;
613
614 basicauthenticators->n_to_start = authenticateChildren;
615
616 basicauthenticators->concurrency = authenticateConcurrency;
617
618 basicauthenticators->ipc_type = IPC_STREAM;
619
620 helperOpenServers(basicauthenticators);
621
622 CBDATA_INIT_TYPE(AuthenticateStateData);
623 }
624 }
625
626 void
627 AuthBasicConfig::registerWithCacheManager(void)
628 {
629 CacheManager::GetInstance()->
630 registerAction("basicauthenticator",
631 "Basic User Authenticator Stats",
632 authenticateBasicStats, 0, 1);
633 }
634
635 void
636 BasicUser::queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
637 {
638 BasicAuthQueueNode *node;
639 node = static_cast<BasicAuthQueueNode *>(xmalloc(sizeof(BasicAuthQueueNode)));
640 assert(node);
641 /* save the details */
642 node->next = auth_queue;
643 auth_queue = node;
644 node->auth_user_request = auth_user_request;
645 node->handler = handler;
646 node->data = cbdataReference(data);
647 }
648
649 /* send the initial data to a basic authenticator module */
650 void
651 AuthBasicUserRequest::module_start(RH * handler, void *data)
652 {
653 basic_data *basic_auth;
654 assert(user()->auth_type == AUTH_BASIC);
655 basic_auth = dynamic_cast<basic_data *>(user());
656 assert(basic_auth != NULL);
657 debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'");
658
659 if (basicConfig.authenticate == NULL) {
660 handler(data, NULL);
661 return;
662 }
663
664 /* check to see if the auth_user already has a request outstanding */
665 if (basic_auth->flags.credentials_ok == 2) {
666 /* there is a request with the same credentials already being verified */
667 basic_auth->queueRequest(this, handler, data);
668 return;
669 }
670
671 basic_auth->submitRequest (this, handler, data);
672 }
673
674 void
675 BasicUser::submitRequest(AuthUserRequest * auth_user_request, RH * handler, void *data)
676 {
677 /* mark the user as haveing verification in progress */
678 flags.credentials_ok = 2;
679 AuthenticateStateData *r = NULL;
680 char buf[8192];
681 char user[1024], pass[1024];
682 r = cbdataAlloc(AuthenticateStateData);
683 r->handler = handler;
684 r->data = cbdataReference(data);
685 r->auth_user_request = auth_user_request;
686 if (basicConfig.utf8) {
687 latin1_to_utf8(user, sizeof(user), username());
688 latin1_to_utf8(pass, sizeof(pass), passwd);
689 xstrncpy(user, rfc1738_escape(user), sizeof(user));
690 xstrncpy(pass, rfc1738_escape(pass), sizeof(pass));
691 } else {
692 xstrncpy(user, rfc1738_escape(username()), sizeof(user));
693 xstrncpy(pass, rfc1738_escape(passwd), sizeof(pass));
694 }
695 snprintf(buf, sizeof(buf), "%s %s\n", user, pass);
696 helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
697 }
698
699 AuthConfig *
700 basicScheme::createConfig()
701 {
702 return &basicConfig;
703 }