]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/basic/auth_basic.cc
consistent formatting
[thirdparty/squid.git] / src / auth / basic / auth_basic.cc
CommitLineData
94439e4e 1
2/*
5dae8514 3 * $Id: auth_basic.cc,v 1.3 2001/01/11 00:01:53 hno Exp $
94439e4e 4 *
5 * DEBUG: section 29 Authenticator
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * the Regents of the University of California. Please see the
16 * COPYRIGHT file for full details. Squid incorporates software
17 * developed and/or copyrighted by other sources. Please see the
18 * CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36/* The functions in this file handle authentication.
37 * They DO NOT perform access control or auditing.
38 * See acl.c for access control and client_side.c for auditing */
39
40
41#include "squid.h"
42#include "auth_basic.h"
43
44static void
45authenticateStateFree(authenticateStateData * r)
46{
47 cbdataFree(r);
48}
49
50/* Basic Scheme */
51
52static HLPCB authenticateBasicHandleReply;
53static AUTHSACTIVE authenticateBasicActive;
54static AUTHSAUTHED authenticateBasicAuthenticated;
55static AUTHSAUTHUSER authenticateBasicAuthenticateUser;
56static AUTHSDIRECTION authenticateBasicDirection;
57static AUTHSDECODE authenticateBasicDecodeAuth;
58static AUTHSDUMP authBasicCfgDump;
59static AUTHSFIXERR authenticateBasicFixErrorHeader;
60static AUTHSFREE authenticateBasicFreeUser;
61static AUTHSFREECONFIG authBasicFreeConfig;
62static AUTHSPARSE authBasicParse;
63static AUTHSINIT authBasicInit;
64static AUTHSSTART authenticateBasicStart;
65static AUTHSSTATS authenticateBasicStats;
66static AUTHSUSERNAME authenticateBasicUsername;
67static AUTHSSHUTDOWN authBasicDone;
68
69static helper *basicauthenticators = NULL;
70
71static auth_basic_config *basicConfig = NULL;
72
73static int authbasic_initialised = 0;
74MemPool *basic_data_pool = NULL;
75
76/*
77 *
78 * Private Functions
79 *
80 */
81
82void
83authBasicDone(void)
84{
94439e4e 85 if (basicauthenticators)
86 helperShutdown(basicauthenticators);
87 authbasic_initialised = 0;
88 if (!shutting_down)
89 return;
5dae8514 90 if (basicauthenticators)
91 helperFree(basicauthenticators);
94439e4e 92 basicauthenticators = NULL;
5dae8514 93 if (basic_data_pool) {
94 memPoolDestroy(basic_data_pool);
95 basic_data_pool = NULL;
96 }
97 debug(29, 2) ("authBasicDone: Basic authentication Shutdown.\n");
94439e4e 98}
99
100void
101authSchemeSetup_basic(authscheme_entry_t * authscheme)
102{
103 assert(!authbasic_initialised);
104 authscheme->Active = authenticateBasicActive;
105 authscheme->parse = authBasicParse;
106 authscheme->dump = authBasicCfgDump;
107 authscheme->init = authBasicInit;
108 authscheme->authAuthenticate = authenticateBasicAuthenticateUser;
109 authscheme->authenticated = authenticateBasicAuthenticated;
110 authscheme->authFixHeader = authenticateBasicFixErrorHeader;
111 authscheme->FreeUser = authenticateBasicFreeUser;
112 authscheme->freeconfig = authBasicFreeConfig;
113 authscheme->authStart = authenticateBasicStart;
114 authscheme->authStats = authenticateBasicStats;
115 authscheme->authUserUsername = authenticateBasicUsername;
116 authscheme->getdirection = authenticateBasicDirection;
117 authscheme->oncloseconnection = NULL;
118 authscheme->decodeauth = authenticateBasicDecodeAuth;
119 authscheme->donefunc = authBasicDone;
120}
121
122int
123authenticateBasicActive()
124{
94439e4e 125 if ((basicConfig != NULL) && (basicConfig->authenticate != NULL) &&
126 (basicConfig->authenticateChildren != 0) && (basicConfig->basicAuthRealm != NULL))
127 return 1;
128 return 0;
129}
130
131int
132authenticateBasicAuthenticated(auth_user_request_t * auth_user_request)
133{
134 basic_data *basic_auth = auth_user_request->auth_user->scheme_data;
135 if ((auth_user_request->auth_user->flags.credentials_ok == 1) && (basic_auth->credentials_checkedtime + basicConfig->credentialsTTL > squid_curtime))
136 return 1;
137 debug(29, 4) ("User not authenticated or credentials need rechecking.\n");
138 return 0;
139}
140
141int
142authenticateBasiccmpUsername(basic_data * u1, basic_data * u2)
143{
144 return strcmp(u1->username, u2->username);
145}
146
147/* log a basic user in
148 */
149static void
150authenticateBasicAuthenticateUser(auth_user_request_t * auth_user_request, request_t * request, ConnStateData * conn, http_hdr_type type)
151{
94439e4e 152 auth_user_t *auth_user;
153 basic_data *basic_auth;
94439e4e 154
155 assert(auth_user_request->auth_user != NULL);
156 auth_user = auth_user_request->auth_user;
157
158 /* if the password is not ok, do an identity */
159 if (auth_user->flags.credentials_ok != 1)
160 return;
161
162 assert(auth_user->scheme_data != NULL);
163 basic_auth = auth_user->scheme_data;
164
165 /* are we about to recheck the credentials externally? */
166 if ((basic_auth->credentials_checkedtime + basicConfig->credentialsTTL) <= squid_curtime) {
167 debug(29, 4) ("authBasicAuthenticate: credentials expired - rechecking\n");
168 return;
169 }
94439e4e 170 /* we have been through the external helper, and the credentials haven't expired */
171 debug(29, 9) ("authenticateBasicAuthenticateuser: user '%s' authenticated\n",
172 basic_auth->username);
173
174 /* Decode now takes care of finding the auth_user struct in the cache */
94439e4e 175 /* after external auth occurs anyway */
176 auth_user->expiretime = current_time.tv_sec;
177 auth_user->ip_expiretime = squid_curtime;
178 return;
179}
180
181int
182authenticateBasicDirection(auth_user_request_t * auth_user_request)
183{
184/* null auth_user is checked for by authenticateDirection */
185 auth_user_t *auth_user = auth_user_request->auth_user;
186 basic_data *basic_auth = auth_user->scheme_data;
187 switch (auth_user->flags.credentials_ok) {
188 case 0: /* not checked */
189 return -1;
190 case 1: /* checked & ok */
191 if (basic_auth->credentials_checkedtime + basicConfig->credentialsTTL <= squid_curtime)
192 return -1;
193 return 0;
194 case 2: /* paused while waiting for a username:password check on another request */
195 return -1;
196 case 3: /* authentication process failed. */
197 return -2;
198 }
199 return -2;
200}
201
202void
203authenticateBasicFixErrorHeader(auth_user_request_t * auth_user_request, HttpReply * rep, http_hdr_type type, request_t * request)
204{
205 if (basicConfig->authenticate) {
206 debug(29, 9) ("authenticateFixErrorHeader: Sending type:%d header: 'Basic realm=\"%s\"'\n", type, basicConfig->basicAuthRealm);
207 httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", basicConfig->basicAuthRealm);
208 }
209}
210
211/* free any allocated configuration details */
212void
213authBasicFreeConfig(authScheme * scheme)
214{
215 if (basicConfig == NULL)
216 return;
217 assert(basicConfig == scheme->scheme_data);
218 if (basicConfig->authenticate)
219 wordlistDestroy(&basicConfig->authenticate);
220 if (basicConfig->basicAuthRealm)
221 safe_free(basicConfig->basicAuthRealm);
222 xfree(basicConfig);
223 basicConfig = NULL;
224}
225
226void
227authenticateBasicFreeUser(auth_user_t * auth_user)
228{
229 basic_data *basic_auth = auth_user->scheme_data;
230 debug(29, 5) ("authenticateBasicFreeUser: Clearing Basic scheme data\n");
231 if (basic_auth->username)
232 xfree(basic_auth->username);
233 if (basic_auth->passwd)
234 xfree(basic_auth->passwd);
235 memPoolFree(basic_data_pool, auth_user->scheme_data);
236 auth_user->scheme_data = NULL;
237}
238
239static void
240authenticateBasicHandleReply(void *data, char *reply)
241{
242 authenticateStateData *r = data;
243 auth_user_t *auth_user;
244 basic_data *basic_auth;
245 auth_basic_queue_node *node, *tmpnode;
246 int valid;
247 char *t = NULL;
248 debug(29, 9) ("authenticateBasicHandleReply: {%s}\n", reply ? reply : "<NULL>");
249 if (reply) {
250 if ((t = strchr(reply, ' ')))
251 *t = '\0';
252 if (*reply == '\0')
253 reply = NULL;
254 }
255 assert(r->auth_user_request != NULL);
256 assert(r->auth_user_request->auth_user->auth_type == AUTH_BASIC);
257 auth_user = r->auth_user_request->auth_user;
258 basic_auth = auth_user->scheme_data;
259 if (reply && (strncasecmp(reply, "OK", 2) == 0))
260 auth_user->flags.credentials_ok = 1;
261 else
262 auth_user->flags.credentials_ok = 3;
263 basic_auth->credentials_checkedtime = squid_curtime;
264 valid = cbdataValid(r->data);
94439e4e 265 if (valid)
266 r->handler(r->data, NULL);
5dae8514 267 cbdataUnlock(r->data);
94439e4e 268 node = basic_auth->auth_queue;
269 while (node) {
270 tmpnode = node->next;
271 valid = cbdataValid(node->data);
94439e4e 272 if (valid)
273 node->handler(node->data, NULL);
5dae8514 274 cbdataUnlock(node->data);
94439e4e 275 xfree(node);
276 node = tmpnode;
277 }
94439e4e 278 authenticateStateFree(r);
279}
280
281static void
282authBasicCfgDump(StoreEntry * entry, const char *name, authScheme * scheme)
283{
284 auth_basic_config *config = scheme->scheme_data;
285 wordlist *list = config->authenticate;
286 storeAppendPrintf(entry, "%s %s", name, "basic");
287 while (list != NULL) {
288 storeAppendPrintf(entry, " %s", list->key);
289 list = list->next;
290 }
291 storeAppendPrintf(entry, "\n%s %s realm %s\n%s %s children %d\n%s %s credentialsttl %d seconds\n",
292 name, "basic", config->basicAuthRealm,
293 name, "basic", config->authenticateChildren,
294 name, "basic", config->credentialsTTL);
295
296}
297
298static void
299authBasicParse(authScheme * scheme, int n_configured, char *param_str)
300{
301 if (scheme->scheme_data == NULL) {
302 assert(basicConfig == NULL);
303 /* this is the first param to be found */
304 scheme->scheme_data = xmalloc(sizeof(auth_basic_config));
305 memset(scheme->scheme_data, 0, sizeof(auth_basic_config));
306 basicConfig = scheme->scheme_data;
307 basicConfig->authenticateChildren = 5;
308 }
309 basicConfig = scheme->scheme_data;
310 if (strcasecmp(param_str, "program") == 0) {
311 parse_wordlist(&basicConfig->authenticate);
312 requirePathnameExists("authparam basic program", basicConfig->authenticate->key);
313 } else if (strcasecmp(param_str, "children") == 0) {
314 parse_int(&basicConfig->authenticateChildren);
315 } else if (strcasecmp(param_str, "realm") == 0) {
316 parse_eol(&basicConfig->basicAuthRealm);
317 } else if (strcasecmp(param_str, "credentialsttl") == 0) {
318 parse_time_t(&basicConfig->credentialsTTL);
319 } else {
320 debug(28, 0) ("unrecognised basic auth scheme parameter '%s'\n", param_str);
321 }
322}
323
324static void
325authenticateBasicStats(StoreEntry * sentry)
326{
327 storeAppendPrintf(sentry, "Basic Authenticator Statistics:\n");
328 helperStats(sentry, basicauthenticators);
329}
330
331CBDATA_TYPE(authenticateStateData);
332
333/* authenticateBasicUsername: return a pointer to the username in the */
334char *
335authenticateBasicUsername(auth_user_t * auth_user)
336{
337 basic_data *basic_auth = auth_user->scheme_data;
338 if (basic_auth)
339 return basic_auth->username;
340 return NULL;
341}
342
343basic_data *
344authBasicDataNew()
345{
346 basic_data *temp;
347 temp = memPoolAlloc(basic_data_pool);
348 assert(temp != NULL);
349 temp->username = NULL;
350 temp->passwd = NULL;
351 temp->auth_queue = NULL;
352 return temp;
353}
354
355void
356authBasicDataFree(basic_data * basic_auth)
357{
358}
359
360auth_user_t *
361authBasicAuthUserFindUsername(const char *username)
362{
363 auth_user_hash_pointer *usernamehash;
364 auth_user_t *auth_user;
365 debug(29, 9) ("authBasicAuthUserFindUsername: Looking for user '%s'\n", username);
366 if (username && (usernamehash = hash_lookup(proxy_auth_username_cache, username))) {
367 while ((usernamehash->auth_user->auth_type != AUTH_BASIC) &&
368 (usernamehash->next))
369 usernamehash = usernamehash->next;
370 auth_user = NULL;
371 if (usernamehash->auth_user->auth_type == AUTH_BASIC) {
372 auth_user = usernamehash->auth_user;
373 }
374 return auth_user;
375 }
376 return NULL;
377}
378
379
380
381/*
382 * Decode a Basic [Proxy-]Auth string, linking the passed auth_user_request structure
383 * to any existing user structure or creating one if needed. Note that just returning
384 * will be treated as "cannot decode credentials". Use the message field to return a
385 * descriptive message to the user.
386 */
387
388static void
389authenticateBasicDecodeAuth(auth_user_request_t * auth_user_request, const char *proxy_auth)
390{
391 char *sent_auth;
392 char *cleartext;
393 basic_data *basic_auth, local_basic;
394 auth_user_t *auth_user;
395 dlink_node *node;
396
397 /* decode the username */
398 /* trim BASIC from string */
399 while (!xisspace(*proxy_auth))
400 proxy_auth++;
401
402 local_basic.passwd = NULL;
403
404 /* Trim leading whitespace before decoding */
405 while (xisspace(*proxy_auth))
406 proxy_auth++;
407 /* username and password */
408 sent_auth = xstrdup(proxy_auth);
409 /* Trim trailing \n before decoding */
410 strtok(sent_auth, "\n");
411 cleartext = uudecode(sent_auth);
412 xfree(sent_auth);
413 /*
414 * Don't allow NL or CR in the credentials.
415 * Oezguer Kesim <oec@codeblau.de>
416 */
417 strtok(cleartext, "\r\n");
418 debug(29, 9) ("authenticateBasicDecodeAuth: cleartext = '%s'\n", cleartext);
419 local_basic.username = xstrndup(cleartext, USER_IDENT_SZ);
420 xfree(cleartext);
421 if ((cleartext = strchr(local_basic.username, ':')) != NULL)
422 *(cleartext)++ = '\0';
423 local_basic.passwd = cleartext;
424 if (cleartext == NULL) {
425 debug(29, 4) ("authenticateBasicDecodeAuth: no password in proxy authorization header '%s'\n",
426 proxy_auth);
427 local_basic.passwd = NULL;
428 auth_user_request->message = xstrdup("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
429 } else if (*cleartext == '\0') {
430 debug(29, 4) ("authenticateBasicDecodeAuth: Disallowing empty password,"
431 "user is '%s'\n", local_basic.username);
432 local_basic.passwd = NULL;
433 auth_user_request->message = xstrdup("Request denied because you provided an empty password. Users MUST have a password.");
434 }
435 /* special case: we have to free the strings for user and password
436 * if we are not returning a filled out structure
437 */
438 if (local_basic.passwd == NULL) {
439 if (local_basic.username) {
440 /* log the username */
441 debug(29, 9) ("authBasicDecodeAuth: Creating new user for logging '%s'\n", local_basic.username);
442 /* new auth_user */
443 auth_user = authenticateAuthUserNew("basic");
444 /* new scheme data */
445 basic_auth = authBasicDataNew();
446 /* save the credentials */
447 basic_auth->username = local_basic.username;
448 /* link the scheme data in */
449 auth_user->scheme_data = basic_auth;
450 /* set the auth_user type */
451 auth_user->auth_type = AUTH_BROKEN;
452 /* link the request to the user */
453 auth_user_request->auth_user = auth_user;
454 /* lock for the auth_user_request link */
455 authenticateAuthUserLock(auth_user);
456 node = dlinkNodeNew();
457 dlinkAdd(auth_user_request, node, &auth_user->requests);
94439e4e 458 }
94439e4e 459 return;
460 } else {
461 local_basic.passwd = xstrndup(cleartext, USER_IDENT_SZ);
462 }
463
464 /* now lookup and see if we have a matching auth_user structure in memory. */
465
466 if ((auth_user = authBasicAuthUserFindUsername(local_basic.username)) == NULL) {
467 /* the user doesn't exist in the username cache yet */
468 debug(29, 9) ("authBasicDecodeAuth: Creating new user '%s'\n", local_basic.username);
469 /* new auth_user */
470 auth_user = authenticateAuthUserNew("basic");
471 /* new scheme data */
472 basic_auth = authBasicDataNew();
473 /* save the credentials */
474 basic_auth->username = local_basic.username;
475 basic_auth->passwd = local_basic.passwd;
476 /* link the scheme data in */
477 auth_user->scheme_data = basic_auth;
478 /* set the auth_user type */
479 auth_user->auth_type = AUTH_BASIC;
480 /* current time for timeouts */
481 auth_user->expiretime = current_time.tv_sec;
482 auth_user->ip_expiretime = squid_curtime;
483
484 /* this auth_user struct is the 'lucky one' to get added to the username cache */
485 /* the requests after this link to the auth_user */
486 /* store user in hash */
487 authenticateUserNameCacheAdd(auth_user);
488 } else {
489 debug(29, 9) ("authBasicDecodeAuth: Found user '%s' in the user cache as '%d'\n", local_basic.username, auth_user);
490 xfree(local_basic.username);
491 basic_auth = auth_user->scheme_data;
492 if (strcmp(local_basic.passwd, basic_auth->passwd)) {
493 debug(29, 4) ("authBasicDecodeAuth: new password found. Updating in user master record and resetting auth state to unchecked\n");
494 auth_user->flags.credentials_ok = 0;
495 xfree(basic_auth->passwd);
496 basic_auth->passwd = local_basic.passwd;
497 } else
498 xfree(local_basic.passwd);
499 }
500 /* link the request to the user */
501 auth_user_request->auth_user = auth_user;
502 /* lock for the auth_user_request link */
503 authenticateAuthUserLock(auth_user);
504 node = dlinkNodeNew();
505 dlinkAdd(auth_user_request, node, &auth_user->requests);
506 return;
507}
508
509/* Initialize helpers and the like for this auth scheme. Called AFTER parsing the
510 * config file */
511static void
512authBasicInit(authScheme * scheme)
513{
514 static int init = 0;
515 if (basicConfig->authenticate) {
516 if (!basic_data_pool)
517 basic_data_pool = memPoolCreate("Basic Scheme User Data", sizeof(basic_data));
518 authbasic_initialised = 1;
519 if (basicauthenticators == NULL)
520 basicauthenticators = helperCreate("basicauthenticator");
521 basicauthenticators->cmdline = basicConfig->authenticate;
522 basicauthenticators->n_to_start = basicConfig->authenticateChildren;
523 basicauthenticators->ipc_type = IPC_TCP_SOCKET;
524 helperOpenServers(basicauthenticators);
525 if (!init) {
526 cachemgrRegister("basicauthenticator",
527 "User Authenticator Stats",
528 authenticateBasicStats, 0, 1);
529 init++;
530 }
531 CBDATA_INIT_TYPE(authenticateStateData);
532 }
533}
534
535/* send the initial data to a basic authenticator module */
536static void
537authenticateBasicStart(auth_user_request_t * auth_user_request, RH * handler, void *data)
538{
539 authenticateStateData *r = NULL;
540 char buf[8192];
541 basic_data *basic_auth;
542 assert(auth_user_request);
543 assert(handler);
544 assert(auth_user_request->auth_user->auth_type == AUTH_BASIC);
545 assert(auth_user_request->auth_user->scheme_data != NULL);
546 basic_auth = auth_user_request->auth_user->scheme_data;
547 debug(29, 9) ("authenticateStart: '%s:%s'\n", basic_auth->username,
548 basic_auth->passwd);
549 if (basicConfig->authenticate == NULL) {
550 handler(data, NULL);
551 return;
552 }
553 /* check to see if the auth_user already has a request outstanding */
554 if (auth_user_request->auth_user->flags.credentials_ok == 2) {
555 /* there is a request with the same credentials already being verified */
556 auth_basic_queue_node *node;
557 node = xmalloc(sizeof(auth_basic_queue_node));
558 assert(node);
559 /* save the details */
560 node->next = basic_auth->auth_queue;
561 basic_auth->auth_queue = node;
562 node->auth_user_request = auth_user_request;
563 node->handler = handler;
564 node->data = data;
565 cbdataLock(data);
566 return;
567 } else {
568 r = CBDATA_ALLOC(authenticateStateData, NULL);
569 r->handler = handler;
570 cbdataLock(data);
571 r->data = data;
572 r->auth_user_request = auth_user_request;
573 /* mark the user as haveing verification in progress */
574 auth_user_request->auth_user->flags.credentials_ok = 2;
575 snprintf(buf, 8192, "%s %s\n", basic_auth->username, basic_auth->passwd);
576 helperSubmit(basicauthenticators, buf, authenticateBasicHandleReply, r);
577 }
578}