2 * DEBUG: section 29 Authenticator
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 /* The functions in this file handle authentication.
34 * They DO NOT perform access control or auditing.
35 * See acl.c for access control and client_side.c for auditing */
38 #include "auth/basic/auth_basic.h"
39 #include "auth/basic/Scheme.h"
40 #include "auth/basic/User.h"
41 #include "auth/basic/UserRequest.h"
42 #include "auth/Gadgets.h"
43 #include "auth/State.h"
46 #include "HttpHeaderTools.h"
47 #include "HttpReply.h"
48 #include "mgr/Registration.h"
50 #include "SquidTime.h"
56 static AUTHSSTATS authenticateBasicStats
;
58 helper
*basicauthenticators
= NULL
;
60 static int authbasic_initialised
= 0;
68 /* internal functions */
71 Auth::Basic::Config::active() const
73 return authbasic_initialised
== 1;
77 Auth::Basic::Config::configured() const
79 if ((authenticateProgram
!= NULL
) && (authenticateChildren
.n_max
!= 0) &&
80 (basicAuthRealm
!= NULL
)) {
81 debugs(29, 9, HERE
<< "returning configured");
85 debugs(29, 9, HERE
<< "returning unconfigured");
90 Auth::Basic::Config::type() const
92 return Auth::Basic::Scheme::GetInstance()->type();
96 Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer auth_user_request
, HttpReply
*rep
, http_hdr_type hdrType
, HttpRequest
* request
)
98 if (authenticateProgram
) {
99 debugs(29, 9, HERE
<< "Sending type:" << hdrType
<< " header: 'Basic realm=\"" << basicAuthRealm
<< "\"'");
100 httpHeaderPutStrf(&rep
->header
, hdrType
, "Basic realm=\"%s\"", basicAuthRealm
);
105 Auth::Basic::Config::rotateHelpers()
107 /* schedule closure of existing helpers */
108 if (basicauthenticators
) {
109 helperShutdown(basicauthenticators
);
112 /* NP: dynamic helper restart will ensure they start up again as needed. */
115 /** shutdown the auth helpers and free any allocated configuration details */
117 Auth::Basic::Config::done()
119 authbasic_initialised
= 0;
121 if (basicauthenticators
) {
122 helperShutdown(basicauthenticators
);
125 delete basicauthenticators
;
126 basicauthenticators
= NULL
;
128 if (authenticateProgram
)
129 wordlistDestroy(&authenticateProgram
);
132 safe_free(basicAuthRealm
);
136 Auth::Basic::Config::dump(StoreEntry
* entry
, const char *name
, Auth::Config
* scheme
)
138 wordlist
*list
= authenticateProgram
;
139 storeAppendPrintf(entry
, "%s %s", name
, "basic");
141 while (list
!= NULL
) {
142 storeAppendPrintf(entry
, " %s", list
->key
);
146 storeAppendPrintf(entry
, "\n");
148 storeAppendPrintf(entry
, "%s basic realm %s\n", name
, basicAuthRealm
);
149 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
);
150 storeAppendPrintf(entry
, "%s basic credentialsttl %d seconds\n", name
, (int) credentialsTTL
);
151 storeAppendPrintf(entry
, "%s basic casesensitive %s\n", name
, casesensitive
? "on" : "off");
154 Auth::Basic::Config::Config() :
155 credentialsTTL( 2*60*60 ),
159 basicAuthRealm
= xstrdup("Squid proxy-caching web server");
162 Auth::Basic::Config::~Config()
164 safe_free(basicAuthRealm
);
168 Auth::Basic::Config::parse(Auth::Config
* scheme
, int n_configured
, char *param_str
)
170 if (strcmp(param_str
, "program") == 0) {
171 if (authenticateProgram
)
172 wordlistDestroy(&authenticateProgram
);
174 parse_wordlist(&authenticateProgram
);
176 requirePathnameExists("auth_param basic program", authenticateProgram
->key
);
177 } else if (strcmp(param_str
, "children") == 0) {
178 authenticateChildren
.parseConfig();
179 } else if (strcmp(param_str
, "realm") == 0) {
180 parse_eol(&basicAuthRealm
);
181 } else if (strcmp(param_str
, "credentialsttl") == 0) {
182 parse_time_t(&credentialsTTL
);
183 } else if (strcmp(param_str
, "casesensitive") == 0) {
184 parse_onoff(&casesensitive
);
185 } else if (strcmp(param_str
, "utf8") == 0) {
188 debugs(29, DBG_CRITICAL
, HERE
<< "unrecognised basic auth scheme parameter '" << param_str
<< "'");
193 authenticateBasicStats(StoreEntry
* sentry
)
195 helperStats(sentry
, basicauthenticators
, "Basic Authenticator Statistics");
198 static Auth::User::Pointer
199 authBasicAuthUserFindUsername(const char *username
)
201 AuthUserHashPointer
*usernamehash
;
202 debugs(29, 9, HERE
<< "Looking for user '" << username
<< "'");
204 if (username
&& (usernamehash
= static_cast<AuthUserHashPointer
*>(hash_lookup(proxy_auth_username_cache
, username
)))) {
205 while (usernamehash
) {
206 if ((usernamehash
->user()->auth_type
== Auth::AUTH_BASIC
) &&
207 !strcmp(username
, (char const *)usernamehash
->key
))
208 return usernamehash
->user();
210 usernamehash
= static_cast<AuthUserHashPointer
*>(usernamehash
->next
);
218 Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader
)
220 const char *proxy_auth
= httpAuthHeader
;
222 /* trim BASIC from string */
223 while (xisgraph(*proxy_auth
))
226 /* Trim leading whitespace before decoding */
227 while (xisspace(*proxy_auth
))
230 /* Trim trailing \n before decoding */
231 // XXX: really? is the \n actually still there? does the header parse not drop it?
232 char *eek
= xstrdup(proxy_auth
);
234 char *cleartext
= uudecode(eek
);
239 * Don't allow NL or CR in the credentials.
240 * Oezguer Kesim <oec@codeblau.de>
242 debugs(29, 9, HERE
<< "'" << cleartext
<< "'");
244 if (strcspn(cleartext
, "\r\n") != strlen(cleartext
)) {
245 debugs(29, DBG_IMPORTANT
, "WARNING: Bad characters in authorization header '" << httpAuthHeader
<< "'");
246 safe_free(cleartext
);
253 * Decode a Basic [Proxy-]Auth string, linking the passed
254 * auth_user_request structure to any existing user structure or creating one
255 * if needed. Note that just returning will be treated as
256 * "cannot decode credentials". Use the message field to return a
257 * descriptive message to the user.
259 Auth::UserRequest::Pointer
260 Auth::Basic::Config::decode(char const *proxy_auth
)
262 Auth::UserRequest::Pointer auth_user_request
= dynamic_cast<Auth::UserRequest
*>(new Auth::Basic::UserRequest
);
263 /* decode the username */
265 // retrieve the cleartext (in a dynamically allocated char*)
266 char *cleartext
= decodeCleartext(proxy_auth
);
268 // empty header? no auth details produced...
270 return auth_user_request
;
272 Auth::User::Pointer lb
;
273 /* permitted because local_basic is purely local function scope. */
274 Auth::Basic::User
*local_basic
= NULL
;
276 char *seperator
= strchr(cleartext
, ':');
278 lb
= local_basic
= new Auth::Basic::User(this);
279 if (seperator
== NULL
) {
280 local_basic
->username(cleartext
);
282 /* terminate the username */
284 local_basic
->username(cleartext
);
285 local_basic
->passwd
= xstrdup(seperator
+1);
289 Tolower((char *)local_basic
->username());
291 if (local_basic
->passwd
== NULL
) {
292 debugs(29, 4, HERE
<< "no password in proxy authorization header '" << proxy_auth
<< "'");
293 auth_user_request
->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
295 if (local_basic
->passwd
[0] == '\0') {
296 debugs(29, 4, HERE
<< "Disallowing empty password. User is '" << local_basic
->username() << "'");
297 safe_free(local_basic
->passwd
);
298 auth_user_request
->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
304 if (!local_basic
->valid()) {
305 lb
->auth_type
= Auth::AUTH_BROKEN
;
306 auth_user_request
->user(lb
);
307 return auth_user_request
;
310 /* now lookup and see if we have a matching auth_user structure in memory. */
311 Auth::User::Pointer auth_user
;
313 if ((auth_user
= authBasicAuthUserFindUsername(lb
->username())) == NULL
) {
314 /* the user doesn't exist in the username cache yet */
315 /* save the credentials */
316 debugs(29, 9, HERE
<< "Creating new user '" << lb
->username() << "'");
317 /* set the auth_user type */
318 lb
->auth_type
= Auth::AUTH_BASIC
;
319 /* current time for timeouts */
320 lb
->expiretime
= current_time
.tv_sec
;
322 /* this basic_user struct is the 'lucky one' to get added to the username cache */
323 /* the requests after this link to the basic_user */
324 /* store user in hash */
325 lb
->addToNameCache();
328 assert(auth_user
!= NULL
);
330 /* replace the current cached password with the new one */
331 Auth::Basic::User
*basic_auth
= dynamic_cast<Auth::Basic::User
*>(auth_user
.getRaw());
333 basic_auth
->updateCached(local_basic
);
334 auth_user
= basic_auth
;
337 /* link the request to the in-cache user */
338 auth_user_request
->user(auth_user
);
339 return auth_user_request
;
342 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
345 Auth::Basic::Config::init(Auth::Config
* schemeCfg
)
347 if (authenticateProgram
) {
348 authbasic_initialised
= 1;
350 if (basicauthenticators
== NULL
)
351 basicauthenticators
= new helper("basicauthenticator");
353 basicauthenticators
->cmdline
= authenticateProgram
;
355 basicauthenticators
->childs
.updateLimits(authenticateChildren
);
357 basicauthenticators
->ipc_type
= IPC_STREAM
;
359 helperOpenServers(basicauthenticators
);
364 Auth::Basic::Config::registerWithCacheManager(void)
366 Mgr::RegisterAction("basicauthenticator",
367 "Basic User Authenticator Stats",
368 authenticateBasicStats
, 0, 1);