]>
Commit | Line | Data |
---|---|---|
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" | |
41 | #include "auth_basic.h" | |
e6ccf245 | 42 | #include "authenticate.h" |
62ee09ca | 43 | #include "CacheManager.h" |
e6ccf245 | 44 | #include "Store.h" |
924f73bc | 45 | #include "HttpReply.h" |
f5691f9c | 46 | #include "basicScheme.h" |
d295d770 | 47 | #include "wordlist.h" |
985c86bc | 48 | #include "SquidTime.h" |
94439e4e | 49 | |
50 | static void | |
e6ccf245 | 51 | authenticateStateFree(AuthenticateStateData * r) |
94439e4e | 52 | { |
53 | cbdataFree(r); | |
54 | } | |
55 | ||
56 | /* Basic Scheme */ | |
57 | ||
58 | static HLPCB authenticateBasicHandleReply; | |
94439e4e | 59 | static AUTHSSTATS authenticateBasicStats; |
94439e4e | 60 | |
61 | static helper *basicauthenticators = NULL; | |
62 | ||
f5691f9c | 63 | static AuthBasicConfig basicConfig; |
94439e4e | 64 | |
65 | static int authbasic_initialised = 0; | |
94439e4e | 66 | |
2d72d4fd | 67 | |
94439e4e | 68 | /* |
69 | * | |
2d72d4fd | 70 | * Public Functions |
94439e4e | 71 | * |
72 | */ | |
73 | ||
2d72d4fd | 74 | /* internal functions */ |
75 | ||
f5691f9c | 76 | /* TODO: move to basicScheme.cc - after all per request and user functions are moved out */ |
77 | void | |
78 | basicScheme::done() | |
2d72d4fd | 79 | { |
f5691f9c | 80 | /* TODO: this should be a Config call. */ |
81 | ||
2d72d4fd | 82 | if (basicauthenticators) |
62e76326 | 83 | helperShutdown(basicauthenticators); |
84 | ||
2d72d4fd | 85 | authbasic_initialised = 0; |
62e76326 | 86 | |
2d72d4fd | 87 | if (!shutting_down) |
62e76326 | 88 | return; |
89 | ||
2d72d4fd | 90 | if (basicauthenticators) |
62e76326 | 91 | helperFree(basicauthenticators); |
92 | ||
2d72d4fd | 93 | basicauthenticators = NULL; |
62e76326 | 94 | |
f5691f9c | 95 | /* XXX Reinstate auth shutdown for dynamic schemes? */ |
4c73ba5f | 96 | debugs(29, DBG_CRITICAL, HERE << "Basic authentication Shutdown."); |
2d72d4fd | 97 | } |
98 | ||
f5691f9c | 99 | bool |
100 | AuthBasicConfig::active() const | |
2d70df72 | 101 | { |
f5691f9c | 102 | return authbasic_initialised == 1; |
2d70df72 | 103 | } |
104 | ||
f5691f9c | 105 | bool |
106 | AuthBasicConfig::configured() const | |
94439e4e | 107 | { |
f5691f9c | 108 | if ((authenticate != NULL) && (authenticateChildren != 0) && |
109 | (basicAuthRealm != NULL)) { | |
4c73ba5f | 110 | debugs(29, 9, HERE << "returning configured"); |
f5691f9c | 111 | return true; |
2d70df72 | 112 | } |
62e76326 | 113 | |
4c73ba5f | 114 | debugs(29, 9, HERE << "returning unconfigured"); |
f5691f9c | 115 | return false; |
94439e4e | 116 | } |
117 | ||
f5691f9c | 118 | const char * |
119 | AuthBasicConfig::type() const | |
94439e4e | 120 | { |
f5691f9c | 121 | return basicScheme::GetInstance().type(); |
122 | } | |
62e76326 | 123 | |
f5691f9c | 124 | AuthBasicUserRequest::AuthBasicUserRequest() : _theUser(NULL) |
125 | {} | |
126 | ||
127 | AuthBasicUserRequest::~AuthBasicUserRequest() | |
128 | {} | |
129 | ||
130 | ||
131 | bool | |
132 | BasicUser::authenticated() const | |
94439e4e | 133 | { |
f5691f9c | 134 | if ((flags.credentials_ok == 1) && (credentials_checkedtime + basicConfig.credentialsTTL > squid_curtime)) |
135 | return true; | |
136 | ||
bf8fe701 | 137 | debugs(29, 4, "User not authenticated or credentials need rechecking."); |
f5691f9c | 138 | |
139 | return false; | |
94439e4e | 140 | } |
62e76326 | 141 | |
f5691f9c | 142 | int |
143 | AuthBasicUserRequest::authenticated() const | |
144 | { | |
145 | BasicUser const *basic_auth = dynamic_cast<BasicUser const *>(user()); | |
7e6e1767 | 146 | assert (basic_auth != NULL); |
f5691f9c | 147 | |
148 | if (basic_auth->authenticated()) | |
149 | return 1; | |
150 | ||
151 | return 0; | |
152 | } | |
94439e4e | 153 | |
154 | /* log a basic user in | |
155 | */ | |
f5691f9c | 156 | void |
486bf0fb | 157 | AuthBasicUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type) |
94439e4e | 158 | { |
f5691f9c | 159 | assert(user() != NULL); |
94439e4e | 160 | |
f5691f9c | 161 | basic_data *basic_auth = dynamic_cast<BasicUser *>(user()); |
9bea1d5b | 162 | |
bd507204 | 163 | /* if the password is not ok, do an identity */ |
62e76326 | 164 | |
17729788 | 165 | if (!basic_auth || basic_auth->flags.credentials_ok != 1) |
62e76326 | 166 | return; |
94439e4e | 167 | |
168 | /* are we about to recheck the credentials externally? */ | |
f5691f9c | 169 | if ((basic_auth->credentials_checkedtime + basicConfig.credentialsTTL) <= squid_curtime) { |
bf8fe701 | 170 | debugs(29, 4, "authBasicAuthenticate: credentials expired - rechecking"); |
62e76326 | 171 | return; |
94439e4e | 172 | } |
62e76326 | 173 | |
94439e4e | 174 | /* we have been through the external helper, and the credentials haven't expired */ |
bf8fe701 | 175 | debugs(29, 9, "authenticateBasicAuthenticateuser: user '" << basic_auth->username() << "' authenticated"); |
94439e4e | 176 | |
f5691f9c | 177 | /* Decode now takes care of finding the AuthUser struct in the cache */ |
94439e4e | 178 | /* after external auth occurs anyway */ |
f5691f9c | 179 | basic_auth->expiretime = current_time.tv_sec; |
62e76326 | 180 | |
94439e4e | 181 | return; |
182 | } | |
183 | ||
184 | int | |
f5691f9c | 185 | AuthBasicUserRequest::module_direction() |
94439e4e | 186 | { |
62e76326 | 187 | /* null auth_user is checked for by authenticateDirection */ |
f5691f9c | 188 | basic_data *basic_auth = dynamic_cast<BasicUser *>(user()); |
189 | assert (basic_auth); | |
62e76326 | 190 | |
bd507204 | 191 | switch (basic_auth->flags.credentials_ok) { |
62e76326 | 192 | |
94439e4e | 193 | case 0: /* not checked */ |
62e76326 | 194 | return -1; |
195 | ||
94439e4e | 196 | case 1: /* checked & ok */ |
62e76326 | 197 | |
f5691f9c | 198 | if (basic_auth->credentials_checkedtime + basicConfig.credentialsTTL <= squid_curtime) |
62e76326 | 199 | return -1; |
200 | ||
201 | return 0; | |
202 | ||
94439e4e | 203 | case 2: /* paused while waiting for a username:password check on another request */ |
62e76326 | 204 | return -1; |
205 | ||
94439e4e | 206 | case 3: /* authentication process failed. */ |
88d3f890 | 207 | return 0; |
94439e4e | 208 | } |
62e76326 | 209 | |
94439e4e | 210 | return -2; |
211 | } | |
212 | ||
213 | void | |
76f142cd | 214 | AuthBasicConfig::fixHeader(AuthUserRequest *auth_user_request, HttpReply *rep, http_hdr_type type, HttpRequest * request) |
94439e4e | 215 | { |
f5691f9c | 216 | if (authenticate) { |
4c73ba5f | 217 | debugs(29, 9, HERE << "Sending type:" << type << " header: 'Basic realm=\"" << basicAuthRealm << "\"'"); |
f5691f9c | 218 | httpHeaderPutStrf(&rep->header, type, "Basic realm=\"%s\"", basicAuthRealm); |
94439e4e | 219 | } |
220 | } | |
221 | ||
222 | /* free any allocated configuration details */ | |
223 | void | |
f5691f9c | 224 | AuthBasicConfig::done() |
94439e4e | 225 | { |
f5691f9c | 226 | if (authenticate) |
227 | wordlistDestroy(&authenticate); | |
62e76326 | 228 | |
f5691f9c | 229 | if (basicAuthRealm) |
230 | safe_free(basicAuthRealm); | |
94439e4e | 231 | } |
232 | ||
f5691f9c | 233 | BasicUser::~BasicUser() |
94439e4e | 234 | { |
4c73ba5f AJ |
235 | safe_free(passwd); |
236 | safe_free(cleartext); | |
94439e4e | 237 | } |
238 | ||
239 | static void | |
240 | authenticateBasicHandleReply(void *data, char *reply) | |
241 | { | |
e6ccf245 | 242 | AuthenticateStateData *r = static_cast<AuthenticateStateData *>(data); |
e6ccf245 | 243 | BasicAuthQueueNode *tmpnode; |
94439e4e | 244 | char *t = NULL; |
fa80a8ef | 245 | void *cbdata; |
4c73ba5f | 246 | debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}"); |
62e76326 | 247 | |
94439e4e | 248 | if (reply) { |
62e76326 | 249 | if ((t = strchr(reply, ' '))) |
0a0c70cd | 250 | *t++ = '\0'; |
62e76326 | 251 | |
252 | if (*reply == '\0') | |
253 | reply = NULL; | |
94439e4e | 254 | } |
62e76326 | 255 | |
94439e4e | 256 | assert(r->auth_user_request != NULL); |
f5691f9c | 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()); | |
62e76326 | 259 | |
2948b229 | 260 | assert(basic_auth != NULL); |
261 | ||
94439e4e | 262 | if (reply && (strncasecmp(reply, "OK", 2) == 0)) |
62e76326 | 263 | basic_auth->flags.credentials_ok = 1; |
0a0c70cd | 264 | else { |
62e76326 | 265 | basic_auth->flags.credentials_ok = 3; |
266 | ||
0a0c70cd | 267 | if (t && *t) |
268 | r->auth_user_request->setDenyMessage(t); | |
269 | } | |
270 | ||
94439e4e | 271 | basic_auth->credentials_checkedtime = squid_curtime; |
62e76326 | 272 | |
fa80a8ef | 273 | if (cbdataReferenceValidDone(r->data, &cbdata)) |
62e76326 | 274 | r->handler(cbdata, NULL); |
275 | ||
fa80a8ef | 276 | cbdataReferenceDone(r->data); |
62e76326 | 277 | |
f2afa96a | 278 | while (basic_auth->auth_queue) { |
62e76326 | 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; | |
94439e4e | 287 | } |
62e76326 | 288 | |
94439e4e | 289 | authenticateStateFree(r); |
290 | } | |
291 | ||
f5691f9c | 292 | void |
293 | AuthBasicConfig::dump(StoreEntry * entry, const char *name, AuthConfig * scheme) | |
94439e4e | 294 | { |
f5691f9c | 295 | wordlist *list = authenticate; |
94439e4e | 296 | storeAppendPrintf(entry, "%s %s", name, "basic"); |
62e76326 | 297 | |
94439e4e | 298 | while (list != NULL) { |
62e76326 | 299 | storeAppendPrintf(entry, " %s", list->key); |
300 | list = list->next; | |
94439e4e | 301 | } |
62e76326 | 302 | |
07eca7e0 | 303 | storeAppendPrintf(entry, "\n"); |
304 | ||
f5691f9c | 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); | |
64658378 | 309 | storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off"); |
94439e4e | 310 | } |
311 | ||
f5691f9c | 312 | AuthBasicConfig::AuthBasicConfig() |
313 | { | |
314 | /* TODO: move into initialisation list */ | |
315 | authenticateChildren = 5; | |
316 | credentialsTTL = 2 * 60 * 60; /* two hours */ | |
75126633 | 317 | basicAuthRealm = xstrdup("Squid proxy-caching web server"); |
f5691f9c | 318 | } |
62e76326 | 319 | |
3845e4c8 | 320 | AuthBasicConfig::~AuthBasicConfig() |
321 | { | |
acde4327 | 322 | safe_free(basicAuthRealm); |
3845e4c8 | 323 | } |
324 | ||
f5691f9c | 325 | void |
326 | AuthBasicConfig::parse(AuthConfig * scheme, int n_configured, char *param_str) | |
327 | { | |
94439e4e | 328 | if (strcasecmp(param_str, "program") == 0) { |
f5691f9c | 329 | if (authenticate) |
330 | wordlistDestroy(&authenticate); | |
62e76326 | 331 | |
f5691f9c | 332 | parse_wordlist(&authenticate); |
62e76326 | 333 | |
f5691f9c | 334 | requirePathnameExists("authparam basic program", authenticate->key); |
94439e4e | 335 | } else if (strcasecmp(param_str, "children") == 0) { |
f5691f9c | 336 | parse_int(&authenticateChildren); |
07eca7e0 | 337 | } else if (strcasecmp(param_str, "concurrency") == 0) { |
f5691f9c | 338 | parse_int(&authenticateConcurrency); |
94439e4e | 339 | } else if (strcasecmp(param_str, "realm") == 0) { |
f5691f9c | 340 | parse_eol(&basicAuthRealm); |
94439e4e | 341 | } else if (strcasecmp(param_str, "credentialsttl") == 0) { |
f5691f9c | 342 | parse_time_t(&credentialsTTL); |
64658378 | 343 | } else if (strcasecmp(param_str, "casesensitive") == 0) { |
344 | parse_onoff(&casesensitive); | |
f741d2f6 HN |
345 | } else if (strcasecmp(param_str, "utf8") == 0) { |
346 | parse_onoff(&utf8); | |
94439e4e | 347 | } else { |
4c73ba5f | 348 | debugs(29, DBG_CRITICAL, HERE << "unrecognised basic auth scheme parameter '" << param_str << "'"); |
94439e4e | 349 | } |
350 | } | |
351 | ||
352 | static void | |
353 | authenticateBasicStats(StoreEntry * sentry) | |
354 | { | |
9522b380 | 355 | helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics"); |
94439e4e | 356 | } |
357 | ||
e6ccf245 | 358 | CBDATA_TYPE(AuthenticateStateData); |
94439e4e | 359 | |
e1f7507e | 360 | static AuthUser * |
94439e4e | 361 | authBasicAuthUserFindUsername(const char *username) |
362 | { | |
e6ccf245 | 363 | AuthUserHashPointer *usernamehash; |
e1f7507e | 364 | debugs(29, 9, HERE << "Looking for user '" << username << "'"); |
62e76326 | 365 | |
e6ccf245 | 366 | if (username && (usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, username)))) { |
62e76326 | 367 | while (usernamehash) { |
f5691f9c | 368 | if ((usernamehash->user()->auth_type == AUTH_BASIC) && |
62e76326 | 369 | !strcmp(username, (char const *)usernamehash->key)) |
f5691f9c | 370 | return usernamehash->user(); |
62e76326 | 371 | |
372 | usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next); | |
373 | } | |
94439e4e | 374 | } |
62e76326 | 375 | |
94439e4e | 376 | return NULL; |
377 | } | |
378 | ||
f5691f9c | 379 | void |
380 | BasicUser::deleteSelf() const | |
381 | { | |
382 | delete this; | |
383 | } | |
94439e4e | 384 | |
f5691f9c | 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 | } | |
62e76326 | 389 | |
35b3bc89 | 390 | bool |
f5691f9c | 391 | BasicUser::decodeCleartext() |
392 | { | |
acde4327 AJ |
393 | char *sent_auth = NULL; |
394 | ||
94439e4e | 395 | /* username and password */ |
f5691f9c | 396 | sent_auth = xstrdup(httpAuthHeader); |
acde4327 | 397 | |
94439e4e | 398 | /* Trim trailing \n before decoding */ |
399 | strtok(sent_auth, "\n"); | |
62e76326 | 400 | |
94439e4e | 401 | cleartext = uudecode(sent_auth); |
62e76326 | 402 | |
acde4327 AJ |
403 | safe_free(sent_auth); |
404 | ||
405 | if (!cleartext) | |
406 | return false; | |
62e76326 | 407 | |
94439e4e | 408 | /* |
409 | * Don't allow NL or CR in the credentials. | |
410 | * Oezguer Kesim <oec@codeblau.de> | |
411 | */ | |
4c73ba5f | 412 | debugs(29, 9, HERE << "'" << cleartext << "'"); |
62e76326 | 413 | |
36a04c15 | 414 | if (strcspn(cleartext, "\r\n") != strlen(cleartext)) { |
4c73ba5f | 415 | debugs(29, 1, HERE << "bad characters in authorization header '" << httpAuthHeader << "'"); |
147c7544 | 416 | safe_free(cleartext); |
417 | return false; | |
36a04c15 | 418 | } |
35b3bc89 | 419 | return true; |
420 | } | |
36a04c15 | 421 | |
35b3bc89 | 422 | void |
423 | BasicUser::extractUsername() | |
424 | { | |
acde4327 | 425 | char * seperator = strchr(cleartext, ':'); |
62e76326 | 426 | |
acde4327 AJ |
427 | if (seperator == NULL) { |
428 | username(cleartext); | |
429 | } else { | |
430 | /* terminate the username */ | |
431 | *seperator = '\0'; | |
432 | ||
433 | username(cleartext); | |
64658378 | 434 | |
acde4327 AJ |
435 | /* replace the colon so we can find the password */ |
436 | *seperator = ':'; | |
437 | } | |
411c6ea3 | 438 | |
64658378 | 439 | if (!basicConfig.casesensitive) |
440 | Tolower((char *)username()); | |
f5691f9c | 441 | } |
62e76326 | 442 | |
f5691f9c | 443 | void |
444 | BasicUser::extractPassword() | |
445 | { | |
acde4327 | 446 | passwd = strchr(cleartext, ':'); |
62e76326 | 447 | |
acde4327 | 448 | if (passwd == NULL) { |
4c73ba5f | 449 | debugs(29, 4, HERE << "no password in proxy authorization header '" << httpAuthHeader << "'"); |
f5691f9c | 450 | passwd = NULL; |
4c73ba5f | 451 | currentRequest->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug"); |
acde4327 AJ |
452 | } else { |
453 | ++passwd; | |
454 | if (*passwd == '\0') { | |
4c73ba5f | 455 | debugs(29, 4, HERE << "Disallowing empty password,user is '" << username() << "'"); |
acde4327 | 456 | passwd = NULL; |
4c73ba5f | 457 | currentRequest->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password."); |
acde4327 AJ |
458 | } else { |
459 | passwd = xstrndup(passwd, USER_IDENT_SZ); | |
460 | } | |
94439e4e | 461 | } |
f5691f9c | 462 | } |
94439e4e | 463 | |
f5691f9c | 464 | void |
465 | BasicUser::decode(char const *proxy_auth, AuthUserRequest *auth_user_request) | |
466 | { | |
467 | currentRequest = auth_user_request; | |
468 | httpAuthHeader = proxy_auth; | |
35b3bc89 | 469 | if (decodeCleartext ()) { |
26ac0430 AJ |
470 | extractUsername(); |
471 | extractPassword(); | |
35b3bc89 | 472 | } |
f5691f9c | 473 | currentRequest = NULL; |
474 | httpAuthHeader = NULL; | |
475 | } | |
476 | ||
477 | bool | |
478 | BasicUser::valid() const | |
479 | { | |
147c7544 | 480 | if (username() == NULL) |
26ac0430 | 481 | return false; |
147c7544 | 482 | if (passwd == NULL) |
26ac0430 | 483 | return false; |
147c7544 | 484 | return true; |
f5691f9c | 485 | } |
94439e4e | 486 | |
f5691f9c | 487 | void |
488 | BasicUser::makeLoggingInstance(AuthBasicUserRequest *auth_user_request) | |
489 | { | |
490 | if (username()) { | |
491 | /* log the username */ | |
4c73ba5f | 492 | debugs(29, 9, HERE << "Creating new user for logging '" << username() << "'"); |
62e76326 | 493 | /* new scheme data */ |
f5691f9c | 494 | BasicUser *basic_auth = new BasicUser(& basicConfig); |
495 | auth_user_request->user(basic_auth); | |
62e76326 | 496 | /* save the credentials */ |
f5691f9c | 497 | basic_auth->username(username()); |
498 | username(NULL); | |
62e76326 | 499 | /* set the auth_user type */ |
f5691f9c | 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 */ | |
4c73ba5f | 510 | debugs(29, 9, HERE << "Creating new user '" << username() << "'"); |
f5691f9c | 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 | { | |
4c73ba5f | 532 | debugs(29, 9, HERE << "Found user '" << from->username() << "' in the user cache as '" << this << "'"); |
f5691f9c | 533 | |
534 | if (strcmp(from->passwd, passwd)) { | |
4c73ba5f | 535 | debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked"); |
f5691f9c | 536 | flags.credentials_ok = 0; |
537 | xfree(passwd); | |
538 | passwd = from->passwd; | |
539 | from->passwd = NULL; | |
540 | } | |
541 | ||
542 | if (flags.credentials_ok == 3) { | |
4c73ba5f | 543 | debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked"); |
f5691f9c | 544 | flags.credentials_ok = 0; |
545 | } | |
546 | } | |
547 | ||
4c73ba5f | 548 | /** |
f5691f9c | 549 | * Decode a Basic [Proxy-]Auth string, linking the passed |
550 | * auth_user_request structure to any existing user structure or creating one | |
26ac0430 AJ |
551 | * if needed. Note that just returning will be treated as |
552 | * "cannot decode credentials". Use the message field to return a | |
f5691f9c | 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 | ||
ba53f4b8 | 562 | while (xisgraph(*proxy_auth)) |
f5691f9c | 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; | |
94439e4e | 576 | } |
62e76326 | 577 | |
f5691f9c | 578 | /* now lookup and see if we have a matching auth_user structure in |
579 | * memory. */ | |
62e76326 | 580 | |
e1f7507e | 581 | AuthUser *auth_user; |
62e76326 | 582 | |
f5691f9c | 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 | } | |
62e76326 | 592 | |
f5691f9c | 593 | /* link the request to the in-cache user */ |
594 | auth_user_request->user(basic_auth); | |
62e76326 | 595 | |
f5691f9c | 596 | basic_auth->addRequest(auth_user_request); |
597 | ||
598 | return auth_user_request; | |
94439e4e | 599 | } |
600 | ||
4c73ba5f | 601 | /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the |
94439e4e | 602 | * config file */ |
f5691f9c | 603 | void |
604 | AuthBasicConfig::init(AuthConfig * scheme) | |
94439e4e | 605 | { |
f5691f9c | 606 | if (authenticate) { |
62e76326 | 607 | authbasic_initialised = 1; |
608 | ||
609 | if (basicauthenticators == NULL) | |
610 | basicauthenticators = helperCreate("basicauthenticator"); | |
611 | ||
f5691f9c | 612 | basicauthenticators->cmdline = authenticate; |
62e76326 | 613 | |
f5691f9c | 614 | basicauthenticators->n_to_start = authenticateChildren; |
62e76326 | 615 | |
f5691f9c | 616 | basicauthenticators->concurrency = authenticateConcurrency; |
07eca7e0 | 617 | |
62e76326 | 618 | basicauthenticators->ipc_type = IPC_STREAM; |
619 | ||
620 | helperOpenServers(basicauthenticators); | |
621 | ||
62e76326 | 622 | CBDATA_INIT_TYPE(AuthenticateStateData); |
94439e4e | 623 | } |
624 | } | |
625 | ||
62ee09ca | 626 | void |
15fab853 | 627 | AuthBasicConfig::registerWithCacheManager(void) |
62ee09ca | 628 | { |
15fab853 | 629 | CacheManager::GetInstance()-> |
26ac0430 AJ |
630 | registerAction("basicauthenticator", |
631 | "Basic User Authenticator Stats", | |
632 | authenticateBasicStats, 0, 1); | |
62ee09ca | 633 | } |
634 | ||
f5691f9c | 635 | void |
76f142cd | 636 | BasicUser::queueRequest(AuthUserRequest * auth_user_request, RH * handler, void *data) |
f5691f9c | 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 | ||
94439e4e | 649 | /* send the initial data to a basic authenticator module */ |
f5691f9c | 650 | void |
651 | AuthBasicUserRequest::module_start(RH * handler, void *data) | |
94439e4e | 652 | { |
94439e4e | 653 | basic_data *basic_auth; |
f5691f9c | 654 | assert(user()->auth_type == AUTH_BASIC); |
655 | basic_auth = dynamic_cast<basic_data *>(user()); | |
2948b229 | 656 | assert(basic_auth != NULL); |
4c73ba5f | 657 | debugs(29, 9, HERE << "'" << basic_auth->username() << ":" << basic_auth->passwd << "'"); |
62e76326 | 658 | |
f5691f9c | 659 | if (basicConfig.authenticate == NULL) { |
62e76326 | 660 | handler(data, NULL); |
661 | return; | |
94439e4e | 662 | } |
62e76326 | 663 | |
94439e4e | 664 | /* check to see if the auth_user already has a request outstanding */ |
bd507204 | 665 | if (basic_auth->flags.credentials_ok == 2) { |
62e76326 | 666 | /* there is a request with the same credentials already being verified */ |
f5691f9c | 667 | basic_auth->queueRequest(this, handler, data); |
62e76326 | 668 | return; |
94439e4e | 669 | } |
f5691f9c | 670 | |
671 | basic_auth->submitRequest (this, handler, data); | |
94439e4e | 672 | } |
f5691f9c | 673 | |
674 | void | |
4c73ba5f | 675 | BasicUser::submitRequest(AuthUserRequest * auth_user_request, RH * handler, void *data) |
f5691f9c | 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; | |
f741d2f6 | 686 | if (basicConfig.utf8) { |
26ac0430 AJ |
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)); | |
f741d2f6 | 691 | } else { |
26ac0430 AJ |
692 | xstrncpy(user, rfc1738_escape(username()), sizeof(user)); |
693 | xstrncpy(pass, rfc1738_escape(passwd), sizeof(pass)); | |
f741d2f6 | 694 | } |
f5691f9c | 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 | } |