]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/basic/auth_basic.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / basic / auth_basic.cc
CommitLineData
94439e4e 1/*
94439e4e 2 * DEBUG: section 29 Authenticator
3 * AUTHOR: Duane Wessels
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
94439e4e 6 * ----------------------------------------------------------
7 *
2b6662ba 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.
94439e4e 16 *
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.
26ac0430 21 *
94439e4e 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.
26ac0430 26 *
94439e4e 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.
30 *
31 */
32
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 */
36
582c2af2 37#include "squid.h"
5817ee13 38#include "auth/basic/auth_basic.h"
616cfc4c 39#include "auth/basic/Scheme.h"
aa110616 40#include "auth/basic/User.h"
616cfc4c 41#include "auth/basic/UserRequest.h"
3ad63615 42#include "auth/Gadgets.h"
a623c828 43#include "auth/State.h"
8a01b99e 44#include "cache_cf.h"
25f98340 45#include "charset.h"
a5bac1d2 46#include "HttpHeaderTools.h"
924f73bc 47#include "HttpReply.h"
602d9612 48#include "mgr/Registration.h"
1fa9b1a7 49#include "rfc1738.h"
602d9612
A
50#include "SquidTime.h"
51#include "Store.h"
25f98340 52#include "uudecode.h"
d295d770 53#include "wordlist.h"
94439e4e 54
94439e4e 55/* Basic Scheme */
94439e4e 56static AUTHSSTATS authenticateBasicStats;
94439e4e 57
fe0a0419 58helper *basicauthenticators = NULL;
94439e4e 59
94439e4e 60static int authbasic_initialised = 0;
94439e4e 61
62/*
63 *
2d72d4fd 64 * Public Functions
94439e4e 65 *
66 */
67
2d72d4fd 68/* internal functions */
69
f5691f9c 70bool
372fccd6 71Auth::Basic::Config::active() const
2d70df72 72{
f5691f9c 73 return authbasic_initialised == 1;
2d70df72 74}
75
f5691f9c 76bool
372fccd6 77Auth::Basic::Config::configured() const
94439e4e 78{
58ee2093 79 if ((authenticateProgram != NULL) && (authenticateChildren.n_max != 0) &&
f5691f9c 80 (basicAuthRealm != NULL)) {
4c73ba5f 81 debugs(29, 9, HERE << "returning configured");
f5691f9c 82 return true;
2d70df72 83 }
62e76326 84
4c73ba5f 85 debugs(29, 9, HERE << "returning unconfigured");
f5691f9c 86 return false;
94439e4e 87}
88
f5691f9c 89const char *
372fccd6 90Auth::Basic::Config::type() const
94439e4e 91{
d6374be6 92 return Auth::Basic::Scheme::GetInstance()->type();
f5691f9c 93}
62e76326 94
94439e4e 95void
c7baff40 96Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer auth_user_request, HttpReply *rep, http_hdr_type hdrType, HttpRequest * request)
94439e4e 97{
58ee2093 98 if (authenticateProgram) {
18ec8500
FC
99 debugs(29, 9, HERE << "Sending type:" << hdrType << " header: 'Basic realm=\"" << basicAuthRealm << "\"'");
100 httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"%s\"", basicAuthRealm);
94439e4e 101 }
102}
103
0bcb6908 104void
372fccd6 105Auth::Basic::Config::rotateHelpers()
0bcb6908
AJ
106{
107 /* schedule closure of existing helpers */
108 if (basicauthenticators) {
109 helperShutdown(basicauthenticators);
110 }
111
112 /* NP: dynamic helper restart will ensure they start up again as needed. */
113}
114
5817ee13 115/** shutdown the auth helpers and free any allocated configuration details */
94439e4e 116void
372fccd6 117Auth::Basic::Config::done()
94439e4e 118{
5817ee13
AJ
119 authbasic_initialised = 0;
120
121 if (basicauthenticators) {
122 helperShutdown(basicauthenticators);
5817ee13
AJ
123 }
124
ed3ef6a8
AJ
125 delete basicauthenticators;
126 basicauthenticators = NULL;
127
58ee2093
AJ
128 if (authenticateProgram)
129 wordlistDestroy(&authenticateProgram);
62e76326 130
f5691f9c 131 if (basicAuthRealm)
132 safe_free(basicAuthRealm);
94439e4e 133}
134
f5691f9c 135void
372fccd6 136Auth::Basic::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme)
94439e4e 137{
58ee2093 138 wordlist *list = authenticateProgram;
94439e4e 139 storeAppendPrintf(entry, "%s %s", name, "basic");
62e76326 140
94439e4e 141 while (list != NULL) {
62e76326 142 storeAppendPrintf(entry, " %s", list->key);
143 list = list->next;
94439e4e 144 }
62e76326 145
07eca7e0 146 storeAppendPrintf(entry, "\n");
147
f5691f9c 148 storeAppendPrintf(entry, "%s basic realm %s\n", name, basicAuthRealm);
404cfda1 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);
f5691f9c 150 storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL);
64658378 151 storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
94439e4e 152}
153
372fccd6 154Auth::Basic::Config::Config() :
5817ee13
AJ
155 credentialsTTL( 2*60*60 ),
156 casesensitive(0),
157 utf8(0)
f5691f9c 158{
75126633 159 basicAuthRealm = xstrdup("Squid proxy-caching web server");
f5691f9c 160}
62e76326 161
372fccd6 162Auth::Basic::Config::~Config()
3845e4c8 163{
acde4327 164 safe_free(basicAuthRealm);
3845e4c8 165}
166
f5691f9c 167void
372fccd6 168Auth::Basic::Config::parse(Auth::Config * scheme, int n_configured, char *param_str)
f5691f9c 169{
a37d6070 170 if (strcmp(param_str, "program") == 0) {
58ee2093
AJ
171 if (authenticateProgram)
172 wordlistDestroy(&authenticateProgram);
62e76326 173
58ee2093 174 parse_wordlist(&authenticateProgram);
62e76326 175
58ee2093 176 requirePathnameExists("auth_param basic program", authenticateProgram->key);
a37d6070 177 } else if (strcmp(param_str, "children") == 0) {
48d54e4d 178 authenticateChildren.parseConfig();
a37d6070 179 } else if (strcmp(param_str, "realm") == 0) {
f5691f9c 180 parse_eol(&basicAuthRealm);
a37d6070 181 } else if (strcmp(param_str, "credentialsttl") == 0) {
f5691f9c 182 parse_time_t(&credentialsTTL);
a37d6070 183 } else if (strcmp(param_str, "casesensitive") == 0) {
64658378 184 parse_onoff(&casesensitive);
a37d6070 185 } else if (strcmp(param_str, "utf8") == 0) {
f741d2f6 186 parse_onoff(&utf8);
94439e4e 187 } else {
4c73ba5f 188 debugs(29, DBG_CRITICAL, HERE << "unrecognised basic auth scheme parameter '" << param_str << "'");
94439e4e 189 }
190}
191
192static void
193authenticateBasicStats(StoreEntry * sentry)
194{
9522b380 195 helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
94439e4e 196}
197
d87154ee 198static Auth::User::Pointer
94439e4e 199authBasicAuthUserFindUsername(const char *username)
200{
e6ccf245 201 AuthUserHashPointer *usernamehash;
e1f7507e 202 debugs(29, 9, HERE << "Looking for user '" << username << "'");
62e76326 203
e6ccf245 204 if (username && (usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, username)))) {
62e76326 205 while (usernamehash) {
616cfc4c 206 if ((usernamehash->user()->auth_type == Auth::AUTH_BASIC) &&
62e76326 207 !strcmp(username, (char const *)usernamehash->key))
f5691f9c 208 return usernamehash->user();
62e76326 209
210 usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
211 }
94439e4e 212 }
62e76326 213
94439e4e 214 return NULL;
215}
216
431aae42 217char *
372fccd6 218Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader)
f5691f9c 219{
431aae42 220 const char *proxy_auth = httpAuthHeader;
acde4327 221
431aae42
AJ
222 /* trim BASIC from string */
223 while (xisgraph(*proxy_auth))
742a021b 224 ++proxy_auth;
62e76326 225
431aae42
AJ
226 /* Trim leading whitespace before decoding */
227 while (xisspace(*proxy_auth))
742a021b 228 ++proxy_auth;
94439e4e 229
431aae42
AJ
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);
233 strtok(eek, "\n");
234 char *cleartext = uudecode(eek);
235 safe_free(eek);
236
237 if (cleartext) {
238 /*
239 * Don't allow NL or CR in the credentials.
240 * Oezguer Kesim <oec@codeblau.de>
241 */
242 debugs(29, 9, HERE << "'" << cleartext << "'");
243
244 if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
245 debugs(29, DBG_IMPORTANT, "WARNING: Bad characters in authorization header '" << httpAuthHeader << "'");
246 safe_free(cleartext);
666f0b34 247 }
35b3bc89 248 }
431aae42 249 return cleartext;
f5691f9c 250}
251
4c73ba5f 252/**
f5691f9c 253 * Decode a Basic [Proxy-]Auth string, linking the passed
254 * auth_user_request structure to any existing user structure or creating one
26ac0430
AJ
255 * if needed. Note that just returning will be treated as
256 * "cannot decode credentials". Use the message field to return a
f5691f9c 257 * descriptive message to the user.
258 */
c7baff40 259Auth::UserRequest::Pointer
372fccd6 260Auth::Basic::Config::decode(char const *proxy_auth)
f5691f9c 261{
c7baff40 262 Auth::UserRequest::Pointer auth_user_request = dynamic_cast<Auth::UserRequest*>(new Auth::Basic::UserRequest);
f5691f9c 263 /* decode the username */
f5691f9c 264
431aae42
AJ
265 // retrieve the cleartext (in a dynamically allocated char*)
266 char *cleartext = decodeCleartext(proxy_auth);
f5691f9c 267
431aae42
AJ
268 // empty header? no auth details produced...
269 if (!cleartext)
270 return auth_user_request;
f5691f9c 271
d87154ee 272 Auth::User::Pointer lb;
431aae42 273 /* permitted because local_basic is purely local function scope. */
aa110616 274 Auth::Basic::User *local_basic = NULL;
431aae42
AJ
275
276 char *seperator = strchr(cleartext, ':');
277
aa110616 278 lb = local_basic = new Auth::Basic::User(this);
431aae42
AJ
279 if (seperator == NULL) {
280 local_basic->username(cleartext);
281 } else {
282 /* terminate the username */
283 *seperator = '\0';
284 local_basic->username(cleartext);
285 local_basic->passwd = xstrdup(seperator+1);
286 }
287
288 if (!casesensitive)
289 Tolower((char *)local_basic->username());
290
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");
294 } else {
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.");
299 }
300 }
f5691f9c 301
431aae42 302 xfree(cleartext);
f5691f9c 303
56a49fda 304 if (!local_basic->valid()) {
616cfc4c 305 lb->auth_type = Auth::AUTH_BROKEN;
431aae42 306 auth_user_request->user(lb);
f5691f9c 307 return auth_user_request;
94439e4e 308 }
62e76326 309
3265364b 310 /* now lookup and see if we have a matching auth_user structure in memory. */
d87154ee 311 Auth::User::Pointer auth_user;
56a49fda 312
431aae42 313 if ((auth_user = authBasicAuthUserFindUsername(lb->username())) == NULL) {
56a49fda
AJ
314 /* the user doesn't exist in the username cache yet */
315 /* save the credentials */
431aae42 316 debugs(29, 9, HERE << "Creating new user '" << lb->username() << "'");
56a49fda 317 /* set the auth_user type */
616cfc4c 318 lb->auth_type = Auth::AUTH_BASIC;
56a49fda 319 /* current time for timeouts */
431aae42 320 lb->expiretime = current_time.tv_sec;
56a49fda
AJ
321
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 */
431aae42 325 lb->addToNameCache();
62e76326 326
431aae42 327 auth_user = lb;
56a49fda 328 assert(auth_user != NULL);
f5691f9c 329 } else {
56a49fda 330 /* replace the current cached password with the new one */
aa110616 331 Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(auth_user.getRaw());
3265364b 332 assert(basic_auth);
56a49fda
AJ
333 basic_auth->updateCached(local_basic);
334 auth_user = basic_auth;
f5691f9c 335 }
62e76326 336
f5691f9c 337 /* link the request to the in-cache user */
56a49fda 338 auth_user_request->user(auth_user);
f5691f9c 339 return auth_user_request;
94439e4e 340}
341
4c73ba5f 342/** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
94439e4e 343 * config file */
f5691f9c 344void
372fccd6 345Auth::Basic::Config::init(Auth::Config * schemeCfg)
94439e4e 346{
58ee2093 347 if (authenticateProgram) {
62e76326 348 authbasic_initialised = 1;
349
350 if (basicauthenticators == NULL)
48d54e4d 351 basicauthenticators = new helper("basicauthenticator");
62e76326 352
58ee2093 353 basicauthenticators->cmdline = authenticateProgram;
62e76326 354
1af735c7 355 basicauthenticators->childs.updateLimits(authenticateChildren);
07eca7e0 356
62e76326 357 basicauthenticators->ipc_type = IPC_STREAM;
358
359 helperOpenServers(basicauthenticators);
94439e4e 360 }
361}
362
62ee09ca 363void
372fccd6 364Auth::Basic::Config::registerWithCacheManager(void)
62ee09ca 365{
8822ebee 366 Mgr::RegisterAction("basicauthenticator",
d9fc6862
A
367 "Basic User Authenticator Stats",
368 authenticateBasicStats, 0, 1);
62ee09ca 369}