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