2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 29 Authenticator */
11 /* The functions in this file handle authentication.
12 * They DO NOT perform access control or auditing.
13 * See acl.c for access control and client_side.c for auditing */
16 #include "auth/basic/Config.h"
17 #include "auth/basic/Scheme.h"
18 #include "auth/basic/User.h"
19 #include "auth/basic/UserRequest.h"
20 #include "auth/Gadgets.h"
21 #include "auth/State.h"
25 #include "HttpHeaderTools.h"
26 #include "HttpReply.h"
27 #include "mgr/Registration.h"
29 #include "SquidTime.h"
36 static AUTHSSTATS authenticateBasicStats
;
38 helper
*basicauthenticators
= NULL
;
40 static int authbasic_initialised
= 0;
48 /* internal functions */
51 Auth::Basic::Config::active() const
53 return authbasic_initialised
== 1;
57 Auth::Basic::Config::configured() const
59 if ((authenticateProgram
!= NULL
) && (authenticateChildren
.n_max
!= 0) && !realm
.isEmpty()) {
60 debugs(29, 9, HERE
<< "returning configured");
64 debugs(29, 9, HERE
<< "returning unconfigured");
69 Auth::Basic::Config::type() const
71 return Auth::Basic::Scheme::GetInstance()->type();
75 Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer
, HttpReply
*rep
, http_hdr_type hdrType
, HttpRequest
*)
77 if (authenticateProgram
) {
78 debugs(29, 9, "Sending type:" << hdrType
<< " header: 'Basic realm=\"" << realm
<< "\"'");
79 httpHeaderPutStrf(&rep
->header
, hdrType
, "Basic realm=\"" SQUIDSBUFPH
"\"", SQUIDSBUFPRINT(realm
));
84 Auth::Basic::Config::rotateHelpers()
86 /* schedule closure of existing helpers */
87 if (basicauthenticators
) {
88 helperShutdown(basicauthenticators
);
91 /* NP: dynamic helper restart will ensure they start up again as needed. */
94 /** shutdown the auth helpers and free any allocated configuration details */
96 Auth::Basic::Config::done()
100 authbasic_initialised
= 0;
102 if (basicauthenticators
) {
103 helperShutdown(basicauthenticators
);
106 delete basicauthenticators
;
107 basicauthenticators
= NULL
;
109 if (authenticateProgram
)
110 wordlistDestroy(&authenticateProgram
);
114 Auth::Basic::Config::dump(StoreEntry
* entry
, const char *name
, Auth::Config
* scheme
) const
116 if (!Auth::Config::dump(entry
, name
, scheme
))
117 return false; // not configured
119 storeAppendPrintf(entry
, "%s basic credentialsttl %d seconds\n", name
, (int) credentialsTTL
);
120 storeAppendPrintf(entry
, "%s basic casesensitive %s\n", name
, casesensitive
? "on" : "off");
121 storeAppendPrintf(entry
, "%s basic utf8 %s\n", name
, utf8
? "on" : "off");
125 Auth::Basic::Config::Config() :
126 credentialsTTL( 2*60*60 ),
130 static const SBuf
defaultRealm("Squid proxy-caching web server");
131 realm
= defaultRealm
;
135 Auth::Basic::Config::parse(Auth::Config
* scheme
, int n_configured
, char *param_str
)
137 if (strcmp(param_str
, "credentialsttl") == 0) {
138 parse_time_t(&credentialsTTL
);
139 } else if (strcmp(param_str
, "casesensitive") == 0) {
140 parse_onoff(&casesensitive
);
141 } else if (strcmp(param_str
, "utf8") == 0) {
144 Auth::Config::parse(scheme
, n_configured
, param_str
);
148 authenticateBasicStats(StoreEntry
* sentry
)
150 helperStats(sentry
, basicauthenticators
, "Basic Authenticator Statistics");
154 Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader
)
156 const char *proxy_auth
= httpAuthHeader
;
158 /* trim BASIC from string */
159 while (xisgraph(*proxy_auth
))
162 /* Trim leading whitespace before decoding */
163 while (xisspace(*proxy_auth
))
166 /* Trim trailing \n before decoding */
167 // XXX: really? is the \n actually still there? does the header parse not drop it?
168 char *eek
= xstrdup(proxy_auth
);
170 char *cleartext
= uudecode(eek
);
175 * Don't allow NL or CR in the credentials.
176 * Oezguer Kesim <oec@codeblau.de>
178 debugs(29, 9, HERE
<< "'" << cleartext
<< "'");
180 if (strcspn(cleartext
, "\r\n") != strlen(cleartext
)) {
181 debugs(29, DBG_IMPORTANT
, "WARNING: Bad characters in authorization header '" << httpAuthHeader
<< "'");
182 safe_free(cleartext
);
189 * Decode a Basic [Proxy-]Auth string, linking the passed
190 * auth_user_request structure to any existing user structure or creating one
191 * if needed. Note that just returning will be treated as
192 * "cannot decode credentials". Use the message field to return a
193 * descriptive message to the user.
195 Auth::UserRequest::Pointer
196 Auth::Basic::Config::decode(char const *proxy_auth
, const char *aRequestRealm
)
198 Auth::UserRequest::Pointer auth_user_request
= dynamic_cast<Auth::UserRequest
*>(new Auth::Basic::UserRequest
);
199 /* decode the username */
201 // retrieve the cleartext (in a dynamically allocated char*)
202 char *cleartext
= decodeCleartext(proxy_auth
);
204 // empty header? no auth details produced...
206 return auth_user_request
;
208 Auth::User::Pointer lb
;
209 /* permitted because local_basic is purely local function scope. */
210 Auth::Basic::User
*local_basic
= NULL
;
212 char *seperator
= strchr(cleartext
, ':');
214 lb
= local_basic
= new Auth::Basic::User(this, aRequestRealm
);
217 /* terminate the username */
219 local_basic
->passwd
= xstrdup(seperator
+1);
224 local_basic
->username(cleartext
);
226 if (local_basic
->passwd
== NULL
) {
227 debugs(29, 4, HERE
<< "no password in proxy authorization header '" << proxy_auth
<< "'");
228 auth_user_request
->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
230 if (local_basic
->passwd
[0] == '\0') {
231 debugs(29, 4, HERE
<< "Disallowing empty password. User is '" << local_basic
->username() << "'");
232 safe_free(local_basic
->passwd
);
233 auth_user_request
->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
239 if (!local_basic
->valid()) {
240 lb
->auth_type
= Auth::AUTH_BROKEN
;
241 auth_user_request
->user(lb
);
242 return auth_user_request
;
245 /* now lookup and see if we have a matching auth_user structure in memory. */
246 Auth::User::Pointer auth_user
;
248 if ((auth_user
= findUserInCache(lb
->userKey(), Auth::AUTH_BASIC
)) == NULL
) {
249 /* the user doesn't exist in the username cache yet */
250 /* save the credentials */
251 debugs(29, 9, HERE
<< "Creating new user '" << lb
->username() << "'");
252 /* set the auth_user type */
253 lb
->auth_type
= Auth::AUTH_BASIC
;
254 /* current time for timeouts */
255 lb
->expiretime
= current_time
.tv_sec
;
257 /* this basic_user struct is the 'lucky one' to get added to the username cache */
258 /* the requests after this link to the basic_user */
259 /* store user in hash */
260 lb
->addToNameCache();
263 assert(auth_user
!= NULL
);
265 /* replace the current cached password with the new one */
266 Auth::Basic::User
*basic_auth
= dynamic_cast<Auth::Basic::User
*>(auth_user
.getRaw());
268 basic_auth
->updateCached(local_basic
);
269 auth_user
= basic_auth
;
272 /* link the request to the in-cache user */
273 auth_user_request
->user(auth_user
);
274 return auth_user_request
;
277 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
280 Auth::Basic::Config::init(Auth::Config
*)
282 if (authenticateProgram
) {
283 authbasic_initialised
= 1;
285 if (basicauthenticators
== NULL
)
286 basicauthenticators
= new helper("basicauthenticator");
288 basicauthenticators
->cmdline
= authenticateProgram
;
290 basicauthenticators
->childs
.updateLimits(authenticateChildren
);
292 basicauthenticators
->ipc_type
= IPC_STREAM
;
294 helperOpenServers(basicauthenticators
);
299 Auth::Basic::Config::registerWithCacheManager(void)
301 Mgr::RegisterAction("basicauthenticator",
302 "Basic User Authenticator Stats",
303 authenticateBasicStats
, 0, 1);