]>
Commit | Line | Data |
---|---|---|
f7f3304a | 1 | #include "squid.h" |
928f3421 | 2 | #include "auth/ntlm/auth_ntlm.h" |
616cfc4c | 3 | #include "auth/ntlm/UserRequest.h" |
d232141d | 4 | #include "auth/State.h" |
928f3421 | 5 | #include "cbdata.h" |
582c2af2 FC |
6 | #include "client_side.h" |
7 | #include "globals.h" | |
928f3421 AJ |
8 | #include "HttpRequest.h" |
9 | #include "SquidTime.h" | |
10 | ||
c7baff40 | 11 | Auth::Ntlm::UserRequest::UserRequest() |
928f3421 AJ |
12 | { |
13 | waiting=0; | |
14 | client_blob=0; | |
15 | server_blob=0; | |
16 | authserver=NULL; | |
7afc3bf2 | 17 | request=NULL; |
928f3421 AJ |
18 | } |
19 | ||
c7baff40 | 20 | Auth::Ntlm::UserRequest::~UserRequest() |
928f3421 | 21 | { |
8bf217bd | 22 | assert(LockCount()==0); |
928f3421 AJ |
23 | safe_free(server_blob); |
24 | safe_free(client_blob); | |
25 | ||
26 | releaseAuthServer(); | |
27 | ||
28 | if (request) { | |
29 | HTTPMSGUNLOCK(request); | |
30 | request = NULL; | |
31 | } | |
32 | } | |
33 | ||
34 | const char * | |
c7baff40 | 35 | Auth::Ntlm::UserRequest::connLastHeader() |
928f3421 AJ |
36 | { |
37 | return NULL; | |
38 | } | |
39 | ||
7afc3bf2 | 40 | int |
c7baff40 | 41 | Auth::Ntlm::UserRequest::authenticated() const |
7afc3bf2 AJ |
42 | { |
43 | if (user() != NULL && user()->credentials() == Auth::Ok) { | |
44 | debugs(29, 9, HERE << "user authenticated."); | |
45 | return 1; | |
46 | } | |
47 | ||
48 | debugs(29, 9, HERE << "user not fully authenticated."); | |
49 | return 0; | |
50 | } | |
51 | ||
51a3dd58 | 52 | Auth::Direction |
c7baff40 | 53 | Auth::Ntlm::UserRequest::module_direction() |
928f3421 | 54 | { |
c7baff40 | 55 | /* null auth_user is checked for by Auth::UserRequest::direction() */ |
928f3421 AJ |
56 | |
57 | if (waiting || client_blob) | |
51a3dd58 | 58 | return Auth::CRED_LOOKUP; /* need helper response to continue */ |
928f3421 | 59 | |
616cfc4c | 60 | if (user()->auth_type != Auth::AUTH_NTLM) |
51a3dd58 | 61 | return Auth::CRED_ERROR; |
928f3421 | 62 | |
d232141d | 63 | switch (user()->credentials()) { |
928f3421 | 64 | |
d87154ee | 65 | case Auth::Handshake: |
928f3421 | 66 | assert(server_blob); |
7afc3bf2 | 67 | return Auth::CRED_CHALLENGE; |
928f3421 | 68 | |
d87154ee | 69 | case Auth::Ok: |
51a3dd58 | 70 | return Auth::CRED_VALID; |
928f3421 | 71 | |
d87154ee | 72 | case Auth::Failed: |
7afc3bf2 | 73 | return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE? |
928f3421 | 74 | |
d232141d AJ |
75 | default: |
76 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials()); | |
51a3dd58 | 77 | return Auth::CRED_ERROR; |
d232141d | 78 | } |
928f3421 AJ |
79 | } |
80 | ||
928f3421 | 81 | void |
4c535e87 | 82 | Auth::Ntlm::UserRequest::module_start(AUTHCB * handler, void *data) |
928f3421 | 83 | { |
7afc3bf2 | 84 | static char buf[MAX_AUTHTOKEN_LEN]; |
928f3421 AJ |
85 | |
86 | assert(data); | |
87 | assert(handler); | |
928f3421 | 88 | |
372fccd6 | 89 | if (static_cast<Auth::Ntlm::Config*>(Auth::Config::Find("ntlm"))->authenticateProgram == NULL) { |
d232141d | 90 | debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured."); |
4c535e87 | 91 | handler(data); |
ec5858ff | 92 | return; |
928f3421 AJ |
93 | } |
94 | ||
7afc3bf2 AJ |
95 | debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'"); |
96 | ||
d87154ee | 97 | if (user()->credentials() == Auth::Pending) { |
7afc3bf2 | 98 | snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here? |
928f3421 | 99 | } else { |
7afc3bf2 | 100 | snprintf(buf, sizeof(buf), "KK %s\n", client_blob); |
928f3421 AJ |
101 | } |
102 | ||
103 | waiting = 1; | |
104 | ||
105 | safe_free(client_blob); | |
c7baff40 | 106 | helperStatefulSubmit(ntlmauthenticators, buf, Auth::Ntlm::UserRequest::HandleReply, |
1c756645 | 107 | new Auth::StateData(this, handler, data), authserver); |
928f3421 AJ |
108 | } |
109 | ||
110 | /** | |
111 | * Atomic action: properly release the NTLM auth helpers which may have been reserved | |
112 | * for this request connections use. | |
113 | */ | |
114 | void | |
c7baff40 | 115 | Auth::Ntlm::UserRequest::releaseAuthServer() |
928f3421 AJ |
116 | { |
117 | if (authserver) { | |
118 | debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'"); | |
119 | helperStatefulReleaseServer(authserver); | |
120 | authserver = NULL; | |
121 | } else | |
122 | debugs(29, 6, HERE << "No NTLM auth server to release."); | |
123 | } | |
124 | ||
928f3421 | 125 | void |
c7baff40 | 126 | Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type) |
928f3421 | 127 | { |
56a49fda | 128 | assert(this); |
928f3421 AJ |
129 | |
130 | /* Check that we are in the client side, where we can generate | |
131 | * auth challenges */ | |
132 | ||
133 | if (conn == NULL || !cbdataReferenceValid(conn)) { | |
d87154ee | 134 | user()->credentials(Auth::Failed); |
7afc3bf2 | 135 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication attempt to perform authentication without a connection!"); |
928f3421 AJ |
136 | return; |
137 | } | |
138 | ||
139 | if (waiting) { | |
7afc3bf2 | 140 | debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication waiting for helper reply!"); |
928f3421 AJ |
141 | return; |
142 | } | |
143 | ||
144 | if (server_blob) { | |
7afc3bf2 | 145 | debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!"); |
928f3421 AJ |
146 | return; |
147 | } | |
148 | ||
149 | /* get header */ | |
7afc3bf2 | 150 | const char *proxy_auth = aRequest->header.getStr(type); |
928f3421 AJ |
151 | |
152 | /* locate second word */ | |
7afc3bf2 | 153 | const char *blob = proxy_auth; |
928f3421 AJ |
154 | |
155 | /* if proxy_auth is actually NULL, we'd better not manipulate it. */ | |
156 | if (blob) { | |
157 | while (xisspace(*blob) && *blob) | |
742a021b | 158 | ++blob; |
928f3421 AJ |
159 | |
160 | while (!xisspace(*blob) && *blob) | |
742a021b | 161 | ++blob; |
928f3421 AJ |
162 | |
163 | while (xisspace(*blob) && *blob) | |
742a021b | 164 | ++blob; |
928f3421 AJ |
165 | } |
166 | ||
d232141d | 167 | switch (user()->credentials()) { |
928f3421 | 168 | |
d87154ee | 169 | case Auth::Unchecked: |
928f3421 | 170 | /* we've received a ntlm request. pass to a helper */ |
7afc3bf2 | 171 | debugs(29, 9, HERE << "auth state ntlm none. Received blob: '" << proxy_auth << "'"); |
d87154ee | 172 | user()->credentials(Auth::Pending); |
928f3421 AJ |
173 | safe_free(client_blob); |
174 | client_blob=xstrdup(blob); | |
cc1e110a AJ |
175 | assert(conn->getAuth() == NULL); |
176 | conn->setAuth(this, "new NTLM handshake request"); | |
928f3421 AJ |
177 | request = aRequest; |
178 | HTTPMSGLOCK(request); | |
179 | break; | |
180 | ||
d87154ee | 181 | case Auth::Pending: |
e0236918 | 182 | debugs(29, DBG_IMPORTANT, HERE << "need to ask helper"); |
928f3421 AJ |
183 | break; |
184 | ||
d87154ee | 185 | case Auth::Handshake: |
928f3421 AJ |
186 | /* we should have received a blob from the client. Hand it off to |
187 | * some helper */ | |
188 | safe_free(client_blob); | |
7afc3bf2 | 189 | client_blob = xstrdup(blob); |
928f3421 AJ |
190 | if (request) |
191 | HTTPMSGUNLOCK(request); | |
192 | request = aRequest; | |
193 | HTTPMSGLOCK(request); | |
194 | break; | |
195 | ||
d87154ee | 196 | case Auth::Ok: |
c7baff40 | 197 | fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n"); |
928f3421 AJ |
198 | break; |
199 | ||
d87154ee | 200 | case Auth::Failed: |
928f3421 | 201 | /* we've failed somewhere in authentication */ |
7afc3bf2 | 202 | debugs(29, 9, HERE << "auth state ntlm failed. " << proxy_auth); |
928f3421 AJ |
203 | break; |
204 | } | |
205 | } | |
206 | ||
207 | void | |
0272dd08 | 208 | Auth::Ntlm::UserRequest::HandleReply(void *data, const HelperReply &reply) |
928f3421 | 209 | { |
1c756645 | 210 | Auth::StateData *r = static_cast<Auth::StateData *>(data); |
928f3421 | 211 | |
0272dd08 | 212 | debugs(29, 8, HERE << "helper: '" << reply.whichServer << "' sent us reply=" << reply); |
928f3421 | 213 | |
1c756645 | 214 | if (!cbdataReferenceValid(r->data)) { |
0272dd08 | 215 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication invalid callback data. helper '" << reply.whichServer << "'."); |
1c756645 | 216 | delete r; |
928f3421 AJ |
217 | return; |
218 | } | |
219 | ||
c7baff40 | 220 | Auth::UserRequest::Pointer auth_user_request = r->auth_user_request; |
928f3421 AJ |
221 | assert(auth_user_request != NULL); |
222 | ||
c7baff40 | 223 | Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw()); |
7afc3bf2 AJ |
224 | assert(lm_request != NULL); |
225 | assert(lm_request->waiting); | |
56a49fda | 226 | |
7afc3bf2 AJ |
227 | lm_request->waiting = 0; |
228 | safe_free(lm_request->client_blob); | |
928f3421 | 229 | |
7afc3bf2 AJ |
230 | assert(auth_user_request->user() != NULL); |
231 | assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM); | |
232 | ||
233 | if (lm_request->authserver == NULL) | |
0272dd08 | 234 | lm_request->authserver = reply.whichServer.get(); // XXX: no locking? |
928f3421 | 235 | else |
e166785a | 236 | assert(reply.whichServer == lm_request->authserver); |
928f3421 | 237 | |
dacb64b9 | 238 | switch (reply.result) { |
0272dd08 | 239 | case HelperReply::TT: |
928f3421 | 240 | /* we have been given a blob to send to the client */ |
7afc3bf2 | 241 | safe_free(lm_request->server_blob); |
e857372a | 242 | lm_request->request->flags.mustKeepalive = true; |
450fe1cb | 243 | if (lm_request->request->flags.proxyKeepalive) { |
cf9f0261 CT |
244 | const char *serverBlob = reply.notes.findFirst("token"); |
245 | lm_request->server_blob = xstrdup(serverBlob); | |
7afc3bf2 | 246 | auth_user_request->user()->credentials(Auth::Handshake); |
928f3421 | 247 | auth_user_request->denyMessage("Authentication in progress"); |
cf9f0261 | 248 | debugs(29, 4, HERE << "Need to challenge the client with a server token: '" << serverBlob << "'"); |
928f3421 | 249 | } else { |
7afc3bf2 | 250 | auth_user_request->user()->credentials(Auth::Failed); |
928f3421 AJ |
251 | auth_user_request->denyMessage("NTLM authentication requires a persistent connection"); |
252 | } | |
0272dd08 AJ |
253 | break; |
254 | ||
dacb64b9 | 255 | case HelperReply::Okay: { |
928f3421 | 256 | /* we're finished, release the helper */ |
cf9f0261 CT |
257 | const char *userLabel = reply.notes.findFirst("user"); |
258 | auth_user_request->user()->username(userLabel); | |
928f3421 | 259 | auth_user_request->denyMessage("Login successful"); |
7afc3bf2 AJ |
260 | safe_free(lm_request->server_blob); |
261 | lm_request->releaseAuthServer(); | |
928f3421 | 262 | |
cf9f0261 | 263 | debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel << "'"); |
928f3421 | 264 | /* connection is authenticated */ |
7afc3bf2 | 265 | debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username()); |
928f3421 AJ |
266 | /* see if this is an existing user with a different proxy_auth |
267 | * string */ | |
7afc3bf2 AJ |
268 | AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username())); |
269 | Auth::User::Pointer local_auth_user = lm_request->user(); | |
616cfc4c | 270 | while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM || |
c6cf8dee | 271 | strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0)) |
928f3421 AJ |
272 | usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next); |
273 | if (usernamehash) { | |
274 | /* we can't seamlessly recheck the username due to the | |
275 | * challenge-response nature of the protocol. | |
58e94342 AJ |
276 | * Just free the temporary auth_user after merging as |
277 | * much of it new state into the existing one as possible */ | |
928f3421 | 278 | usernamehash->user()->absorb(local_auth_user); |
7afc3bf2 | 279 | /* from here on we are working with the original cached credentials. */ |
928f3421 | 280 | local_auth_user = usernamehash->user(); |
58e94342 | 281 | auth_user_request->user(local_auth_user); |
928f3421 AJ |
282 | } else { |
283 | /* store user in hash's */ | |
284 | local_auth_user->addToNameCache(); | |
928f3421 AJ |
285 | } |
286 | /* set these to now because this is either a new login from an | |
287 | * existing user or a new user */ | |
288 | local_auth_user->expiretime = current_time.tv_sec; | |
7afc3bf2 | 289 | auth_user_request->user()->credentials(Auth::Ok); |
7bbefa01 | 290 | debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << auth_user_request->user()->username() << "'"); |
0272dd08 | 291 | } |
dacb64b9 | 292 | break; |
7afc3bf2 | 293 | |
7bbefa01 | 294 | case HelperReply::Error: { |
928f3421 | 295 | /* authentication failure (wrong password, etc.) */ |
cf9f0261 | 296 | const char *errNote = reply.notes.find("message"); |
7bbefa01 | 297 | if (errNote != NULL) |
cf9f0261 | 298 | auth_user_request->denyMessage(errNote); |
7bbefa01 AJ |
299 | else |
300 | auth_user_request->denyMessage("NTLM Authentication denied with no reason given"); | |
7afc3bf2 AJ |
301 | auth_user_request->user()->credentials(Auth::Failed); |
302 | safe_free(lm_request->server_blob); | |
303 | lm_request->releaseAuthServer(); | |
87a122b0 | 304 | debugs(29, 4, "Failed validating user via NTLM. Result: " << reply); |
7bbefa01 AJ |
305 | } |
306 | break; | |
0272dd08 AJ |
307 | |
308 | case HelperReply::Unknown: | |
309 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << reply.whichServer << "' crashed!."); | |
0272dd08 AJ |
310 | /* continue to the next case */ |
311 | ||
7bbefa01 | 312 | case HelperReply::BrokenHelper: { |
928f3421 AJ |
313 | /* TODO kick off a refresh process. This can occur after a YR or after |
314 | * a KK. If after a YR release the helper and resubmit the request via | |
315 | * Authenticate NTLM start. | |
316 | * If after a KK deny the user's request w/ 407 and mark the helper as | |
317 | * Needing YR. */ | |
cf9f0261 | 318 | const char *errNote = reply.notes.find("message"); |
7bbefa01 AJ |
319 | if (reply.result == HelperReply::Unknown) |
320 | auth_user_request->denyMessage("Internal Error"); | |
321 | else if (errNote != NULL) | |
cf9f0261 | 322 | auth_user_request->denyMessage(errNote); |
7bbefa01 AJ |
323 | else |
324 | auth_user_request->denyMessage("NTLM Authentication failed with no reason given"); | |
d87154ee | 325 | auth_user_request->user()->credentials(Auth::Failed); |
7afc3bf2 AJ |
326 | safe_free(lm_request->server_blob); |
327 | lm_request->releaseAuthServer(); | |
87a122b0 | 328 | debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Result: " << reply); |
7bbefa01 AJ |
329 | } |
330 | break; | |
928f3421 AJ |
331 | } |
332 | ||
7afc3bf2 AJ |
333 | if (lm_request->request) { |
334 | HTTPMSGUNLOCK(lm_request->request); | |
335 | lm_request->request = NULL; | |
928f3421 | 336 | } |
4c535e87 | 337 | r->handler(r->data); |
1c756645 | 338 | delete r; |
928f3421 | 339 | } |