]>
Commit | Line | Data |
---|---|---|
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 | 36 | static AUTHSSTATS authenticateBasicStats; |
94439e4e | 37 | |
fe0a0419 | 38 | helper *basicauthenticators = NULL; |
94439e4e | 39 | |
94439e4e | 40 | static int authbasic_initialised = 0; |
94439e4e | 41 | |
42 | /* | |
43 | * | |
2d72d4fd | 44 | * Public Functions |
94439e4e | 45 | * |
46 | */ | |
47 | ||
2d72d4fd | 48 | /* internal functions */ |
49 | ||
f5691f9c | 50 | bool |
372fccd6 | 51 | Auth::Basic::Config::active() const |
2d70df72 | 52 | { |
f5691f9c | 53 | return authbasic_initialised == 1; |
2d70df72 | 54 | } |
55 | ||
f5691f9c | 56 | bool |
372fccd6 | 57 | Auth::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 | 68 | const char * |
372fccd6 | 69 | Auth::Basic::Config::type() const |
94439e4e | 70 | { |
d6374be6 | 71 | return Auth::Basic::Scheme::GetInstance()->type(); |
f5691f9c | 72 | } |
62e76326 | 73 | |
94439e4e | 74 | void |
ced8def3 | 75 | Auth::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 | 83 | void |
372fccd6 | 84 | Auth::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 | 95 | void |
372fccd6 | 96 | Auth::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 |
113 | bool |
114 | Auth::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 | 125 | Auth::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 | 134 | void |
372fccd6 | 135 | Auth::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 | ||
147 | static void | |
148 | authenticateBasicStats(StoreEntry * sentry) | |
149 | { | |
9522b380 | 150 | helperStats(sentry, basicauthenticators, "Basic Authenticator Statistics"); |
94439e4e | 151 | } |
152 | ||
431aae42 | 153 | char * |
372fccd6 | 154 | Auth::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 | 195 | Auth::UserRequest::Pointer |
d4806c91 | 196 | Auth::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 | 279 | void |
ced8def3 | 280 | Auth::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 | 298 | void |
372fccd6 | 299 | Auth::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 |