PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/squid-3.2.0.16/src/auth/ntlm/UserRequest.cc

#
C++ | 345 lines | 250 code | 59 blank | 36 comment | 65 complexity | 3c95dc7c46fc4752fa2f266cf150911b MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. #include "squid.h"
  2. #include "auth/ntlm/auth_ntlm.h"
  3. #include "auth/ntlm/UserRequest.h"
  4. #include "auth/State.h"
  5. #include "cbdata.h"
  6. #include "HttpRequest.h"
  7. #include "SquidTime.h"
  8. Auth::Ntlm::UserRequest::UserRequest()
  9. {
  10. waiting=0;
  11. client_blob=0;
  12. server_blob=0;
  13. authserver=NULL;
  14. request=NULL;
  15. }
  16. Auth::Ntlm::UserRequest::~UserRequest()
  17. {
  18. assert(RefCountCount()==0);
  19. safe_free(server_blob);
  20. safe_free(client_blob);
  21. releaseAuthServer();
  22. if (request) {
  23. HTTPMSGUNLOCK(request);
  24. request = NULL;
  25. }
  26. }
  27. const char *
  28. Auth::Ntlm::UserRequest::connLastHeader()
  29. {
  30. return NULL;
  31. }
  32. int
  33. Auth::Ntlm::UserRequest::authenticated() const
  34. {
  35. if (user() != NULL && user()->credentials() == Auth::Ok) {
  36. debugs(29, 9, HERE << "user authenticated.");
  37. return 1;
  38. }
  39. debugs(29, 9, HERE << "user not fully authenticated.");
  40. return 0;
  41. }
  42. Auth::Direction
  43. Auth::Ntlm::UserRequest::module_direction()
  44. {
  45. /* null auth_user is checked for by Auth::UserRequest::direction() */
  46. if (waiting || client_blob)
  47. return Auth::CRED_LOOKUP; /* need helper response to continue */
  48. if (user()->auth_type != Auth::AUTH_NTLM)
  49. return Auth::CRED_ERROR;
  50. switch (user()->credentials()) {
  51. case Auth::Handshake:
  52. assert(server_blob);
  53. return Auth::CRED_CHALLENGE;
  54. case Auth::Ok:
  55. return Auth::CRED_VALID;
  56. case Auth::Failed:
  57. return Auth::CRED_ERROR; // XXX: really? not VALID or CHALLENGE?
  58. default:
  59. debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication in unexpected state: " << user()->credentials());
  60. return Auth::CRED_ERROR;
  61. }
  62. }
  63. void
  64. Auth::Ntlm::UserRequest::module_start(RH * handler, void *data)
  65. {
  66. static char buf[MAX_AUTHTOKEN_LEN];
  67. assert(data);
  68. assert(handler);
  69. if (static_cast<Auth::Ntlm::Config*>(Auth::Config::Find("ntlm"))->authenticateProgram == NULL) {
  70. debugs(29, DBG_CRITICAL, "ERROR: NTLM Start: no NTLM program configured.");
  71. handler(data, NULL);
  72. return;
  73. }
  74. debugs(29, 8, HERE << "credentials state is '" << user()->credentials() << "'");
  75. if (user()->credentials() == Auth::Pending) {
  76. snprintf(buf, sizeof(buf), "YR %s\n", client_blob); //CHECKME: can ever client_blob be 0 here?
  77. } else {
  78. snprintf(buf, sizeof(buf), "KK %s\n", client_blob);
  79. }
  80. waiting = 1;
  81. safe_free(client_blob);
  82. helperStatefulSubmit(ntlmauthenticators, buf, Auth::Ntlm::UserRequest::HandleReply,
  83. new Auth::StateData(this, handler, data), authserver);
  84. }
  85. /**
  86. * Atomic action: properly release the NTLM auth helpers which may have been reserved
  87. * for this request connections use.
  88. */
  89. void
  90. Auth::Ntlm::UserRequest::releaseAuthServer()
  91. {
  92. if (authserver) {
  93. debugs(29, 6, HERE << "releasing NTLM auth server '" << authserver << "'");
  94. helperStatefulReleaseServer(authserver);
  95. authserver = NULL;
  96. } else
  97. debugs(29, 6, HERE << "No NTLM auth server to release.");
  98. }
  99. void
  100. Auth::Ntlm::UserRequest::onConnectionClose(ConnStateData *conn)
  101. {
  102. assert(conn != NULL);
  103. debugs(29, 8, HERE << "closing connection '" << conn << "' (this is '" << this << "')");
  104. if (conn->auth_user_request == NULL) {
  105. debugs(29, 8, HERE << "no auth_user_request");
  106. return;
  107. }
  108. releaseAuthServer();
  109. /* unlock the connection based lock */
  110. debugs(29, 9, HERE << "Unlocking auth_user from the connection '" << conn << "'.");
  111. conn->auth_user_request = NULL;
  112. }
  113. void
  114. Auth::Ntlm::UserRequest::authenticate(HttpRequest * aRequest, ConnStateData * conn, http_hdr_type type)
  115. {
  116. assert(this);
  117. /* Check that we are in the client side, where we can generate
  118. * auth challenges */
  119. if (conn == NULL || !cbdataReferenceValid(conn)) {
  120. user()->credentials(Auth::Failed);
  121. debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication attempt to perform authentication without a connection!");
  122. return;
  123. }
  124. if (waiting) {
  125. debugs(29, DBG_IMPORTANT, "WARNING: NTLM Authentication waiting for helper reply!");
  126. return;
  127. }
  128. if (server_blob) {
  129. debugs(29, 2, HERE << "need to challenge client '" << server_blob << "'!");
  130. return;
  131. }
  132. /* get header */
  133. const char *proxy_auth = aRequest->header.getStr(type);
  134. /* locate second word */
  135. const char *blob = proxy_auth;
  136. /* if proxy_auth is actually NULL, we'd better not manipulate it. */
  137. if (blob) {
  138. while (xisspace(*blob) && *blob)
  139. blob++;
  140. while (!xisspace(*blob) && *blob)
  141. blob++;
  142. while (xisspace(*blob) && *blob)
  143. blob++;
  144. }
  145. switch (user()->credentials()) {
  146. case Auth::Unchecked:
  147. /* we've received a ntlm request. pass to a helper */
  148. debugs(29, 9, HERE << "auth state ntlm none. Received blob: '" << proxy_auth << "'");
  149. user()->credentials(Auth::Pending);
  150. safe_free(client_blob);
  151. client_blob=xstrdup(blob);
  152. assert(conn->auth_user_request == NULL);
  153. conn->auth_user_request = this;
  154. request = aRequest;
  155. HTTPMSGLOCK(request);
  156. break;
  157. case Auth::Pending:
  158. debugs(29, 1, HERE << "need to ask helper");
  159. break;
  160. case Auth::Handshake:
  161. /* we should have received a blob from the client. Hand it off to
  162. * some helper */
  163. safe_free(client_blob);
  164. client_blob = xstrdup(blob);
  165. if (request)
  166. HTTPMSGUNLOCK(request);
  167. request = aRequest;
  168. HTTPMSGLOCK(request);
  169. break;
  170. case Auth::Ok:
  171. fatal("Auth::Ntlm::UserRequest::authenticate: unexpect auth state DONE! Report a bug to the squid developers.\n");
  172. break;
  173. case Auth::Failed:
  174. /* we've failed somewhere in authentication */
  175. debugs(29, 9, HERE << "auth state ntlm failed. " << proxy_auth);
  176. break;
  177. }
  178. }
  179. void
  180. Auth::Ntlm::UserRequest::HandleReply(void *data, void *lastserver, char *reply)
  181. {
  182. Auth::StateData *r = static_cast<Auth::StateData *>(data);
  183. char *blob;
  184. debugs(29, 8, HERE << "helper: '" << lastserver << "' sent us '" << (reply ? reply : "<NULL>") << "'");
  185. if (!cbdataReferenceValid(r->data)) {
  186. debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication invalid callback data. helper '" << lastserver << "'.");
  187. delete r;
  188. return;
  189. }
  190. if (!reply) {
  191. debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication Helper '" << lastserver << "' crashed!.");
  192. reply = (char *)"BH Internal error";
  193. }
  194. Auth::UserRequest::Pointer auth_user_request = r->auth_user_request;
  195. assert(auth_user_request != NULL);
  196. Auth::Ntlm::UserRequest *lm_request = dynamic_cast<Auth::Ntlm::UserRequest *>(auth_user_request.getRaw());
  197. assert(lm_request != NULL);
  198. assert(lm_request->waiting);
  199. lm_request->waiting = 0;
  200. safe_free(lm_request->client_blob);
  201. assert(auth_user_request->user() != NULL);
  202. assert(auth_user_request->user()->auth_type == Auth::AUTH_NTLM);
  203. if (lm_request->authserver == NULL)
  204. lm_request->authserver = static_cast<helper_stateful_server*>(lastserver);
  205. else
  206. assert(lm_request->authserver == lastserver);
  207. /* seperate out the useful data */
  208. blob = strchr(reply, ' ');
  209. if (blob)
  210. blob++;
  211. if (strncasecmp(reply, "TT ", 3) == 0) {
  212. /* we have been given a blob to send to the client */
  213. safe_free(lm_request->server_blob);
  214. lm_request->request->flags.must_keepalive = 1;
  215. if (lm_request->request->flags.proxy_keepalive) {
  216. lm_request->server_blob = xstrdup(blob);
  217. auth_user_request->user()->credentials(Auth::Handshake);
  218. auth_user_request->denyMessage("Authentication in progress");
  219. debugs(29, 4, HERE << "Need to challenge the client with a server blob '" << blob << "'");
  220. } else {
  221. auth_user_request->user()->credentials(Auth::Failed);
  222. auth_user_request->denyMessage("NTLM authentication requires a persistent connection");
  223. }
  224. } else if (strncasecmp(reply, "AF ", 3) == 0) {
  225. /* we're finished, release the helper */
  226. auth_user_request->user()->username(blob);
  227. auth_user_request->denyMessage("Login successful");
  228. safe_free(lm_request->server_blob);
  229. lm_request->releaseAuthServer();
  230. debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << blob << "'");
  231. /* connection is authenticated */
  232. debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
  233. /* see if this is an existing user with a different proxy_auth
  234. * string */
  235. AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->username()));
  236. Auth::User::Pointer local_auth_user = lm_request->user();
  237. while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM ||
  238. strcmp(usernamehash->user()->username(), auth_user_request->user()->username()) != 0))
  239. usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
  240. if (usernamehash) {
  241. /* we can't seamlessly recheck the username due to the
  242. * challenge-response nature of the protocol.
  243. * Just free the temporary auth_user after merging as
  244. * much of it new state into the existing one as possible */
  245. usernamehash->user()->absorb(local_auth_user);
  246. /* from here on we are working with the original cached credentials. */
  247. local_auth_user = usernamehash->user();
  248. auth_user_request->user(local_auth_user);
  249. } else {
  250. /* store user in hash's */
  251. local_auth_user->addToNameCache();
  252. }
  253. /* set these to now because this is either a new login from an
  254. * existing user or a new user */
  255. local_auth_user->expiretime = current_time.tv_sec;
  256. auth_user_request->user()->credentials(Auth::Ok);
  257. debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << blob << "'");
  258. } else if (strncasecmp(reply, "NA ", 3) == 0) {
  259. /* authentication failure (wrong password, etc.) */
  260. auth_user_request->denyMessage(blob);
  261. auth_user_request->user()->credentials(Auth::Failed);
  262. safe_free(lm_request->server_blob);
  263. lm_request->releaseAuthServer();
  264. debugs(29, 4, HERE << "Failed validating user via NTLM. Error returned '" << blob << "'");
  265. } else if (strncasecmp(reply, "BH ", 3) == 0) {
  266. /* TODO kick off a refresh process. This can occur after a YR or after
  267. * a KK. If after a YR release the helper and resubmit the request via
  268. * Authenticate NTLM start.
  269. * If after a KK deny the user's request w/ 407 and mark the helper as
  270. * Needing YR. */
  271. auth_user_request->denyMessage(blob);
  272. auth_user_request->user()->credentials(Auth::Failed);
  273. safe_free(lm_request->server_blob);
  274. lm_request->releaseAuthServer();
  275. debugs(29, DBG_IMPORTANT, "ERROR: NTLM Authentication validating user. Error returned '" << reply << "'");
  276. } else {
  277. /* protocol error */
  278. fatalf("authenticateNTLMHandleReply: *** Unsupported helper response ***, '%s'\n", reply);
  279. }
  280. if (lm_request->request) {
  281. HTTPMSGUNLOCK(lm_request->request);
  282. lm_request->request = NULL;
  283. }
  284. r->handler(r->data, NULL);
  285. delete r;
  286. }