]> git.ipfire.org Git - thirdparty/squid.git/blame - src/auth/basic/Config.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / auth / basic / Config.cc
CommitLineData
94439e4e 1/*
bde978a6 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
94439e4e 3 *
bbc27441
AJ
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.
94439e4e 7 */
8
bbc27441
AJ
9/* DEBUG: section 29 Authenticator */
10
94439e4e 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 */
14
582c2af2 15#include "squid.h"
12daeef6 16#include "auth/basic/Config.h"
616cfc4c 17#include "auth/basic/Scheme.h"
aa110616 18#include "auth/basic/User.h"
616cfc4c 19#include "auth/basic/UserRequest.h"
3ad63615 20#include "auth/Gadgets.h"
a623c828 21#include "auth/State.h"
8a01b99e 22#include "cache_cf.h"
25f98340 23#include "charset.h"
24438ec5 24#include "helper.h"
a5bac1d2 25#include "HttpHeaderTools.h"
924f73bc 26#include "HttpReply.h"
602d9612 27#include "mgr/Registration.h"
1fa9b1a7 28#include "rfc1738.h"
602d9612
A
29#include "SquidTime.h"
30#include "Store.h"
ed6e9fb9 31#include "util.h"
25f98340 32#include "uudecode.h"
d295d770 33#include "wordlist.h"
94439e4e 34
94439e4e 35/* Basic Scheme */
94439e4e 36static AUTHSSTATS authenticateBasicStats;
94439e4e 37
fe0a0419 38helper *basicauthenticators = NULL;
94439e4e 39
94439e4e 40static int authbasic_initialised = 0;
94439e4e 41
42/*
43 *
2d72d4fd 44 * Public Functions
94439e4e 45 *
46 */
47
2d72d4fd 48/* internal functions */
49
f5691f9c 50bool
372fccd6 51Auth::Basic::Config::active() const
2d70df72 52{
f5691f9c 53 return authbasic_initialised == 1;
2d70df72 54}
55
f5691f9c 56bool
372fccd6 57Auth::Basic::Config::configured() const
94439e4e 58{
ec980001 59 if ((authenticateProgram != NULL) && (authenticateChildren.n_max != 0) && !realm.isEmpty()) {
4c73ba5f 60 debugs(29, 9, HERE << "returning configured");
f5691f9c 61 return true;
2d70df72 62 }
62e76326 63
4c73ba5f 64 debugs(29, 9, HERE << "returning unconfigured");
f5691f9c 65 return false;
94439e4e 66}
67
f5691f9c 68const char *
372fccd6 69Auth::Basic::Config::type() const
94439e4e 70{
d6374be6 71 return Auth::Basic::Scheme::GetInstance()->type();
f5691f9c 72}
62e76326 73
94439e4e 74void
ced8def3 75Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer, HttpReply *rep, http_hdr_type hdrType, HttpRequest *)
94439e4e 76{
58ee2093 77 if (authenticateProgram) {
ec980001
AJ
78 debugs(29, 9, "Sending type:" << hdrType << " header: 'Basic realm=\"" << realm << "\"'");
79 httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"" SQUIDSBUFPH "\"", SQUIDSBUFPRINT(realm));
94439e4e 80 }
81}
82
0bcb6908 83void
372fccd6 84Auth::Basic::Config::rotateHelpers()
0bcb6908
AJ
85{
86 /* schedule closure of existing helpers */
87 if (basicauthenticators) {
88 helperShutdown(basicauthenticators);
89 }
90
91 /* NP: dynamic helper restart will ensure they start up again as needed. */
92}
93
5817ee13 94/** shutdown the auth helpers and free any allocated configuration details */
94439e4e 95void
372fccd6 96Auth::Basic::Config::done()
94439e4e 97{
d4806c91
CT
98 Auth::Config::done();
99
5817ee13
AJ
100 authbasic_initialised = 0;
101
102 if (basicauthenticators) {
103 helperShutdown(basicauthenticators);
5817ee13
AJ
104 }
105
ed3ef6a8
AJ
106 delete basicauthenticators;
107 basicauthenticators = NULL;
108
58ee2093
AJ
109 if (authenticateProgram)
110 wordlistDestroy(&authenticateProgram);
94439e4e 111}
112
3616c90c
AJ
113bool
114Auth::Basic::Config::dump(StoreEntry * entry, const char *name, Auth::Config * scheme) const
94439e4e 115{
3616c90c
AJ
116 if (!Auth::Config::dump(entry, name, scheme))
117 return false; // not configured
07eca7e0 118
f5691f9c 119 storeAppendPrintf(entry, "%s basic credentialsttl %d seconds\n", name, (int) credentialsTTL);
64658378 120 storeAppendPrintf(entry, "%s basic casesensitive %s\n", name, casesensitive ? "on" : "off");
3616c90c
AJ
121 storeAppendPrintf(entry, "%s basic utf8 %s\n", name, utf8 ? "on" : "off");
122 return true;
94439e4e 123}
124
372fccd6 125Auth::Basic::Config::Config() :
f53969cc
SM
126 credentialsTTL( 2*60*60 ),
127 casesensitive(0),
128 utf8(0)
f5691f9c 129{
ec980001
AJ
130 static const SBuf defaultRealm("Squid proxy-caching web server");
131 realm = defaultRealm;
3845e4c8 132}
133
f5691f9c 134void
372fccd6 135Auth::Basic::Config::parse(Auth::Config * scheme, int n_configured, char *param_str)
f5691f9c 136{
3616c90c 137 if (strcmp(param_str, "credentialsttl") == 0) {
f5691f9c 138 parse_time_t(&credentialsTTL);
a37d6070 139 } else if (strcmp(param_str, "casesensitive") == 0) {
64658378 140 parse_onoff(&casesensitive);
a37d6070 141 } else if (strcmp(param_str, "utf8") == 0) {
f741d2f6 142 parse_onoff(&utf8);
d4806c91
CT
143 } else
144 Auth::Config::parse(scheme, n_configured, param_str);
94439e4e 145}
146
147static void
148authenticateBasicStats(StoreEntry * sentry)
149{
9522b380 150 helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics");
94439e4e 151}
152
431aae42 153char *
372fccd6 154Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader)
f5691f9c 155{
431aae42 156 const char *proxy_auth = httpAuthHeader;
acde4327 157
431aae42
AJ
158 /* trim BASIC from string */
159 while (xisgraph(*proxy_auth))
742a021b 160 ++proxy_auth;
62e76326 161
431aae42
AJ
162 /* Trim leading whitespace before decoding */
163 while (xisspace(*proxy_auth))
742a021b 164 ++proxy_auth;
94439e4e 165
431aae42
AJ
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);
169 strtok(eek, "\n");
170 char *cleartext = uudecode(eek);
171 safe_free(eek);
172
173 if (cleartext) {
174 /*
175 * Don't allow NL or CR in the credentials.
176 * Oezguer Kesim <oec@codeblau.de>
177 */
178 debugs(29, 9, HERE << "'" << cleartext << "'");
179
180 if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
181 debugs(29, DBG_IMPORTANT, "WARNING: Bad characters in authorization header '" << httpAuthHeader << "'");
182 safe_free(cleartext);
666f0b34 183 }
35b3bc89 184 }
431aae42 185 return cleartext;
f5691f9c 186}
187
4c73ba5f 188/**
f5691f9c 189 * Decode a Basic [Proxy-]Auth string, linking the passed
190 * auth_user_request structure to any existing user structure or creating one
26ac0430
AJ
191 * if needed. Note that just returning will be treated as
192 * "cannot decode credentials". Use the message field to return a
f5691f9c 193 * descriptive message to the user.
194 */
c7baff40 195Auth::UserRequest::Pointer
d4806c91 196Auth::Basic::Config::decode(char const *proxy_auth, const char *aRequestRealm)
f5691f9c 197{
c7baff40 198 Auth::UserRequest::Pointer auth_user_request = dynamic_cast<Auth::UserRequest*>(new Auth::Basic::UserRequest);
f5691f9c 199 /* decode the username */
f5691f9c 200
431aae42
AJ
201 // retrieve the cleartext (in a dynamically allocated char*)
202 char *cleartext = decodeCleartext(proxy_auth);
f5691f9c 203
431aae42
AJ
204 // empty header? no auth details produced...
205 if (!cleartext)
206 return auth_user_request;
f5691f9c 207
d87154ee 208 Auth::User::Pointer lb;
431aae42 209 /* permitted because local_basic is purely local function scope. */
aa110616 210 Auth::Basic::User *local_basic = NULL;
431aae42
AJ
211
212 char *seperator = strchr(cleartext, ':');
213
d4806c91
CT
214 lb = local_basic = new Auth::Basic::User(this, aRequestRealm);
215
216 if (seperator) {
431aae42
AJ
217 /* terminate the username */
218 *seperator = '\0';
431aae42
AJ
219 local_basic->passwd = xstrdup(seperator+1);
220 }
221
222 if (!casesensitive)
d4806c91
CT
223 Tolower(cleartext);
224 local_basic->username(cleartext);
225
431aae42
AJ
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");
229 } else {
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.");
234 }
235 }
f5691f9c 236
431aae42 237 xfree(cleartext);
f5691f9c 238
56a49fda 239 if (!local_basic->valid()) {
616cfc4c 240 lb->auth_type = Auth::AUTH_BROKEN;
431aae42 241 auth_user_request->user(lb);
f5691f9c 242 return auth_user_request;
94439e4e 243 }
62e76326 244
3265364b 245 /* now lookup and see if we have a matching auth_user structure in memory. */
d87154ee 246 Auth::User::Pointer auth_user;
56a49fda 247
d8d76b36 248 if ((auth_user = findUserInCache(lb->userKey(), Auth::AUTH_BASIC)) == NULL) {
56a49fda
AJ
249 /* the user doesn't exist in the username cache yet */
250 /* save the credentials */
431aae42 251 debugs(29, 9, HERE << "Creating new user '" << lb->username() << "'");
56a49fda 252 /* set the auth_user type */
616cfc4c 253 lb->auth_type = Auth::AUTH_BASIC;
56a49fda 254 /* current time for timeouts */
431aae42 255 lb->expiretime = current_time.tv_sec;
56a49fda
AJ
256
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 */
431aae42 260 lb->addToNameCache();
62e76326 261
431aae42 262 auth_user = lb;
56a49fda 263 assert(auth_user != NULL);
f5691f9c 264 } else {
56a49fda 265 /* replace the current cached password with the new one */
aa110616 266 Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(auth_user.getRaw());
3265364b 267 assert(basic_auth);
56a49fda
AJ
268 basic_auth->updateCached(local_basic);
269 auth_user = basic_auth;
f5691f9c 270 }
62e76326 271
f5691f9c 272 /* link the request to the in-cache user */
56a49fda 273 auth_user_request->user(auth_user);
f5691f9c 274 return auth_user_request;
94439e4e 275}
276
4c73ba5f 277/** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
94439e4e 278 * config file */
f5691f9c 279void
ced8def3 280Auth::Basic::Config::init(Auth::Config *)
94439e4e 281{
58ee2093 282 if (authenticateProgram) {
62e76326 283 authbasic_initialised = 1;
284
285 if (basicauthenticators == NULL)
48d54e4d 286 basicauthenticators = new helper("basicauthenticator");
62e76326 287
58ee2093 288 basicauthenticators->cmdline = authenticateProgram;
62e76326 289
1af735c7 290 basicauthenticators->childs.updateLimits(authenticateChildren);
07eca7e0 291
62e76326 292 basicauthenticators->ipc_type = IPC_STREAM;
293
294 helperOpenServers(basicauthenticators);
94439e4e 295 }
296}
297
62ee09ca 298void
372fccd6 299Auth::Basic::Config::registerWithCacheManager(void)
62ee09ca 300{
8822ebee 301 Mgr::RegisterAction("basicauthenticator",
d9fc6862
A
302 "Basic User Authenticator Stats",
303 authenticateBasicStats, 0, 1);
62ee09ca 304}
f53969cc 305