]> git.ipfire.org Git - thirdparty/squid.git/blob - src/auth/basic/Config.cc
4237b6e3ef199803a4f496043e6ec8a782661322
[thirdparty/squid.git] / src / auth / basic / Config.cc
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9 /* DEBUG: section 29 Authenticator */
10
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
15 #include "squid.h"
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"
23 #include "cache_cf.h"
24 #include "charset.h"
25 #include "helper.h"
26 #include "HttpHeaderTools.h"
27 #include "HttpReply.h"
28 #include "mgr/Registration.h"
29 #include "rfc1738.h"
30 #include "SquidTime.h"
31 #include "Store.h"
32 #include "util.h"
33 #include "uudecode.h"
34 #include "wordlist.h"
35
36 /* Basic Scheme */
37 static AUTHSSTATS authenticateBasicStats;
38
39 helper *basicauthenticators = NULL;
40
41 static int authbasic_initialised = 0;
42
43 /*
44 *
45 * Public Functions
46 *
47 */
48
49 /* internal functions */
50
51 bool
52 Auth::Basic::Config::active() const
53 {
54 return authbasic_initialised == 1;
55 }
56
57 bool
58 Auth::Basic::Config::configured() const
59 {
60 if ((authenticateProgram != NULL) && (authenticateChildren.n_max != 0) && !realm.isEmpty()) {
61 debugs(29, 9, HERE << "returning configured");
62 return true;
63 }
64
65 debugs(29, 9, HERE << "returning unconfigured");
66 return false;
67 }
68
69 const char *
70 Auth::Basic::Config::type() const
71 {
72 return Auth::Basic::Scheme::GetInstance()->type();
73 }
74
75 void
76 Auth::Basic::Config::fixHeader(Auth::UserRequest::Pointer, HttpReply *rep, Http::HdrType hdrType, HttpRequest *)
77 {
78 if (authenticateProgram) {
79 debugs(29, 9, "Sending type:" << hdrType << " header: 'Basic realm=\"" << realm << "\"'");
80 httpHeaderPutStrf(&rep->header, hdrType, "Basic realm=\"" SQUIDSBUFPH "\"", SQUIDSBUFPRINT(realm));
81 }
82 }
83
84 void
85 Auth::Basic::Config::rotateHelpers()
86 {
87 /* schedule closure of existing helpers */
88 if (basicauthenticators) {
89 helperShutdown(basicauthenticators);
90 }
91
92 /* NP: dynamic helper restart will ensure they start up again as needed. */
93 }
94
95 /** shutdown the auth helpers and free any allocated configuration details */
96 void
97 Auth::Basic::Config::done()
98 {
99 Auth::SchemeConfig::done();
100
101 authbasic_initialised = 0;
102
103 if (basicauthenticators) {
104 helperShutdown(basicauthenticators);
105 }
106
107 delete basicauthenticators;
108 basicauthenticators = NULL;
109
110 if (authenticateProgram)
111 wordlistDestroy(&authenticateProgram);
112 }
113
114 bool
115 Auth::Basic::Config::dump(StoreEntry * entry, const char *name, Auth::SchemeConfig * scheme) const
116 {
117 if (!Auth::SchemeConfig::dump(entry, name, scheme))
118 return false; // not configured
119
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");
123 return true;
124 }
125
126 Auth::Basic::Config::Config() :
127 credentialsTTL( 2*60*60 ),
128 casesensitive(0),
129 utf8(0)
130 {
131 static const SBuf defaultRealm("Squid proxy-caching web server");
132 realm = defaultRealm;
133 }
134
135 void
136 Auth::Basic::Config::parse(Auth::SchemeConfig * scheme, int n_configured, char *param_str)
137 {
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) {
143 parse_onoff(&utf8);
144 } else
145 Auth::SchemeConfig::parse(scheme, n_configured, param_str);
146 }
147
148 static void
149 authenticateBasicStats(StoreEntry * sentry)
150 {
151 if (basicauthenticators)
152 basicauthenticators->packStatsInto(sentry, "Basic Authenticator Statistics");
153 }
154
155 char *
156 Auth::Basic::Config::decodeCleartext(const char *httpAuthHeader)
157 {
158 const char *proxy_auth = httpAuthHeader;
159
160 /* trim BASIC from string */
161 while (xisgraph(*proxy_auth))
162 ++proxy_auth;
163
164 /* Trim leading whitespace before decoding */
165 while (xisspace(*proxy_auth))
166 ++proxy_auth;
167
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);
171 strtok(eek, "\n");
172 char *cleartext = uudecode(eek);
173 safe_free(eek);
174
175 if (cleartext) {
176 /*
177 * Don't allow NL or CR in the credentials.
178 * Oezguer Kesim <oec@codeblau.de>
179 */
180 debugs(29, 9, HERE << "'" << cleartext << "'");
181
182 if (strcspn(cleartext, "\r\n") != strlen(cleartext)) {
183 debugs(29, DBG_IMPORTANT, "WARNING: Bad characters in authorization header '" << httpAuthHeader << "'");
184 safe_free(cleartext);
185 }
186 }
187 return cleartext;
188 }
189
190 /**
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.
196 */
197 Auth::UserRequest::Pointer
198 Auth::Basic::Config::decode(char const *proxy_auth, const char *aRequestRealm)
199 {
200 Auth::UserRequest::Pointer auth_user_request = dynamic_cast<Auth::UserRequest*>(new Auth::Basic::UserRequest);
201 /* decode the username */
202
203 // retrieve the cleartext (in a dynamically allocated char*)
204 char *cleartext = decodeCleartext(proxy_auth);
205
206 // empty header? no auth details produced...
207 if (!cleartext)
208 return auth_user_request;
209
210 Auth::User::Pointer lb;
211 /* permitted because local_basic is purely local function scope. */
212 Auth::Basic::User *local_basic = NULL;
213
214 char *separator = strchr(cleartext, ':');
215
216 lb = local_basic = new Auth::Basic::User(this, aRequestRealm);
217
218 if (separator) {
219 /* terminate the username */
220 *separator = '\0';
221 local_basic->passwd = xstrdup(separator+1);
222 }
223
224 if (!casesensitive)
225 Tolower(cleartext);
226 local_basic->username(cleartext);
227
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");
231 } else {
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.");
236 }
237 }
238
239 xfree(cleartext);
240
241 if (!local_basic->valid()) {
242 lb->auth_type = Auth::AUTH_BROKEN;
243 auth_user_request->user(lb);
244 return auth_user_request;
245 }
246
247 /* now lookup and see if we have a matching auth_user structure in memory. */
248 Auth::User::Pointer auth_user;
249
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;
258
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();
263
264 auth_user = lb;
265 assert(auth_user != NULL);
266 } else {
267 /* replace the current cached password with the new one */
268 Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(auth_user.getRaw());
269 assert(basic_auth);
270 basic_auth->updateCached(local_basic);
271 auth_user = basic_auth;
272 }
273
274 /* link the request to the in-cache user */
275 auth_user_request->user(auth_user);
276 return auth_user_request;
277 }
278
279 /** Initialize helpers and the like for this auth scheme. Called AFTER parsing the
280 * config file */
281 void
282 Auth::Basic::Config::init(Auth::SchemeConfig *)
283 {
284 if (authenticateProgram) {
285 authbasic_initialised = 1;
286
287 if (basicauthenticators == NULL)
288 basicauthenticators = new helper("basicauthenticator");
289
290 basicauthenticators->cmdline = authenticateProgram;
291
292 basicauthenticators->childs.updateLimits(authenticateChildren);
293
294 basicauthenticators->ipc_type = IPC_STREAM;
295
296 helperOpenServers(basicauthenticators);
297 }
298 }
299
300 void
301 Auth::Basic::Config::registerWithCacheManager(void)
302 {
303 Mgr::RegisterAction("basicauthenticator",
304 "Basic User Authenticator Stats",
305 authenticateBasicStats, 0, 1);
306 }
307