2 * Copyright (C) 1996-2016 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/CredentialsCache.h"
21 #include "auth/Gadgets.h"
22 #include "auth/State.h"
26 #include "HttpHeaderTools.h"
27 #include "HttpReply.h"
28 #include "mgr/Registration.h"
30 #include "SquidTime.h"
37 static AUTHSSTATS authenticateBasicStats
;
39 helper
*basicauthenticators
= NULL
;
41 static int authbasic_initialised
= 0;
49 /* internal functions */
52 Auth::Basic::Config::active() const
54 return authbasic_initialised
== 1;
58 Auth::Basic::Config::configured() const
60 if ((authenticateProgram
!= NULL
) && (authenticateChildren
.n_max
!= 0) && !realm
.isEmpty()) {
61 debugs(29, 9, HERE
<< "returning configured");
65 debugs(29, 9, HERE
<< "returning unconfigured");
70 Auth::Basic::Config::type() const
72 return Auth::Basic::Scheme::GetInstance()->type();
76 Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer
, HttpReply
*rep
, Http::HdrType hdrType
, HttpRequest
*)
78 if (authenticateProgram
) {
79 debugs(29, 9, "Sending type:" << hdrType
<< " header: 'Basic realm=\"" << realm
<< "\"'");
80 httpHeaderPutStrf(&rep
->header
, hdrType
, "Basic realm=\"" SQUIDSBUFPH
"\"", SQUIDSBUFPRINT(realm
));
85 Auth::Basic::Config::rotateHelpers()
87 /* schedule closure of existing helpers */
88 if (basicauthenticators
) {
89 helperShutdown(basicauthenticators
);
92 /* NP: dynamic helper restart will ensure they start up again as needed. */
95 /** shutdown the auth helpers and free any allocated configuration details */
97 Auth::Basic::Config::done()
99 Auth::SchemeConfig::done();
101 authbasic_initialised
= 0;
103 if (basicauthenticators
) {
104 helperShutdown(basicauthenticators
);
107 delete basicauthenticators
;
108 basicauthenticators
= NULL
;
110 if (authenticateProgram
)
111 wordlistDestroy(&authenticateProgram
);
115 Auth::Basic::Config::dump(StoreEntry
* entry
, const char *name
, Auth::SchemeConfig
* scheme
) const
117 if (!Auth::SchemeConfig::dump(entry
, name
, scheme
))
118 return false; // not configured
120 storeAppendPrintf(entry
, "%s basic credentialsttl %d seconds\n", name
, (int) credentialsTTL
);
121 storeAppendPrintf(entry
, "%s basic casesensitive %s\n", name
, casesensitive
? "on" : "off");
122 storeAppendPrintf(entry
, "%s basic utf8 %s\n", name
, utf8
? "on" : "off");
126 Auth::Basic::Config::Config() :
127 credentialsTTL( 2*60*60 ),
131 static const SBuf
defaultRealm("Squid proxy-caching web server");
132 realm
= defaultRealm
;
136 Auth::Basic::Config::parse(Auth::SchemeConfig
* scheme
, int n_configured
, char *param_str
)
138 if (strcmp(param_str
, "credentialsttl") == 0) {
139 parse_time_t(&credentialsTTL
);
140 } else if (strcmp(param_str
, "casesensitive") == 0) {
141 parse_onoff(&casesensitive
);
142 } else if (strcmp(param_str
, "utf8") == 0) {
145 Auth::SchemeConfig::parse(scheme
, n_configured
, param_str
);
149 authenticateBasicStats(StoreEntry
* sentry
)
151 if (basicauthenticators
)
152 basicauthenticators
->packStatsInto(sentry
, "Basic Authenticator Statistics");
156 Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader
)
158 const char *proxy_auth
= httpAuthHeader
;
160 /* trim BASIC from string */
161 while (xisgraph(*proxy_auth
))
164 /* Trim leading whitespace before decoding */
165 while (xisspace(*proxy_auth
))
168 /* Trim trailing \n before decoding */
169 // XXX: really? is the \n actually still there? does the header parse not drop it?
170 char *eek
= xstrdup(proxy_auth
);
172 char *cleartext
= uudecode(eek
);
177 * Don't allow NL or CR in the credentials.
178 * Oezguer Kesim <oec@codeblau.de>
180 debugs(29, 9, HERE
<< "'" << cleartext
<< "'");
182 if (strcspn(cleartext
, "\r\n") != strlen(cleartext
)) {
183 debugs(29, DBG_IMPORTANT
, "WARNING: Bad characters in authorization header '" << httpAuthHeader
<< "'");
184 safe_free(cleartext
);
191 * Decode a Basic [Proxy-]Auth string, linking the passed
192 * auth_user_request structure to any existing user structure or creating one
193 * if needed. Note that just returning will be treated as
194 * "cannot decode credentials". Use the message field to return a
195 * descriptive message to the user.
197 Auth::UserRequest::Pointer
198 Auth::Basic::Config::decode(char const *proxy_auth
, const char *aRequestRealm
)
200 Auth::UserRequest::Pointer auth_user_request
= dynamic_cast<Auth::UserRequest
*>(new Auth::Basic::UserRequest
);
201 /* decode the username */
203 // retrieve the cleartext (in a dynamically allocated char*)
204 char *cleartext
= decodeCleartext(proxy_auth
);
206 // empty header? no auth details produced...
208 return auth_user_request
;
210 Auth::User::Pointer lb
;
211 /* permitted because local_basic is purely local function scope. */
212 Auth::Basic::User
*local_basic
= NULL
;
214 char *separator
= strchr(cleartext
, ':');
216 lb
= local_basic
= new Auth::Basic::User(this, aRequestRealm
);
219 /* terminate the username */
221 local_basic
->passwd
= xstrdup(separator
+1);
226 local_basic
->username(cleartext
);
228 if (local_basic
->passwd
== NULL
) {
229 debugs(29, 4, HERE
<< "no password in proxy authorization header '" << proxy_auth
<< "'");
230 auth_user_request
->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
232 if (local_basic
->passwd
[0] == '\0') {
233 debugs(29, 4, HERE
<< "Disallowing empty password. User is '" << local_basic
->username() << "'");
234 safe_free(local_basic
->passwd
);
235 auth_user_request
->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
241 if (!local_basic
->valid()) {
242 lb
->auth_type
= Auth::AUTH_BROKEN
;
243 auth_user_request
->user(lb
);
244 return auth_user_request
;
247 /* now lookup and see if we have a matching auth_user structure in memory. */
248 Auth::User::Pointer auth_user
;
250 if (!(auth_user
= Auth::Basic::User::Cache()->lookup(lb
->userKey()))) {
251 /* the user doesn't exist in the username cache yet */
252 /* save the credentials */
253 debugs(29, 9, HERE
<< "Creating new user '" << lb
->username() << "'");
254 /* set the auth_user type */
255 lb
->auth_type
= Auth::AUTH_BASIC
;
256 /* current time for timeouts */
257 lb
->expiretime
= current_time
.tv_sec
;
259 /* this basic_user struct is the 'lucky one' to get added to the username cache */
260 /* the requests after this link to the basic_user */
261 /* store user in hash */
262 lb
->addToNameCache();
265 assert(auth_user
!= NULL
);
267 /* replace the current cached password with the new one */
268 Auth::Basic::User
*basic_auth
= dynamic_cast<Auth::Basic::User
*>(auth_user
.getRaw());
270 basic_auth
->updateCached(local_basic
);
271 auth_user
= basic_auth
;
274 /* link the request to the in-cache user */
275 auth_user_request
->user(auth_user
);
276 return auth_user_request
;
279 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
282 Auth::Basic::Config::init(Auth::SchemeConfig
*)
284 if (authenticateProgram
) {
285 authbasic_initialised
= 1;
287 if (basicauthenticators
== NULL
)
288 basicauthenticators
= new helper("basicauthenticator");
290 basicauthenticators
->cmdline
= authenticateProgram
;
292 basicauthenticators
->childs
.updateLimits(authenticateChildren
);
294 basicauthenticators
->ipc_type
= IPC_STREAM
;
296 helperOpenServers(basicauthenticators
);
301 Auth::Basic::Config::registerWithCacheManager(void)
303 Mgr::RegisterAction("basicauthenticator",
304 "Basic User Authenticator Stats",
305 authenticateBasicStats
, 0, 1);