PageRenderTime 52ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/src/auth/digest/UserRequest.cc

https://github.com/dkurochkin/squid
C++ | 315 lines | 228 code | 59 blank | 28 comment | 71 complexity | 6f9f8df889348434bfa556d322937846 MD5 | raw file
  1. #include "config.h"
  2. #include "auth/digest/auth_digest.h"
  3. #include "auth/digest/User.h"
  4. #include "auth/digest/UserRequest.h"
  5. #include "auth/State.h"
  6. #include "charset.h"
  7. #include "HttpReply.h"
  8. #include "HttpRequest.h"
  9. #include "SquidTime.h"
  10. AuthDigestUserRequest::AuthDigestUserRequest() :
  11. nonceb64(NULL),
  12. cnonce(NULL),
  13. realm(NULL),
  14. pszPass(NULL),
  15. algorithm(NULL),
  16. pszMethod(NULL),
  17. qop(NULL),
  18. uri(NULL),
  19. response(NULL),
  20. nonce(NULL)
  21. {}
  22. /**
  23. * Delete the digest request structure.
  24. * Does NOT delete related AuthUser structures
  25. */
  26. AuthDigestUserRequest::~AuthDigestUserRequest()
  27. {
  28. assert(RefCountCount()==0);
  29. safe_free(nonceb64);
  30. safe_free(cnonce);
  31. safe_free(realm);
  32. safe_free(pszPass);
  33. safe_free(algorithm);
  34. safe_free(pszMethod);
  35. safe_free(qop);
  36. safe_free(uri);
  37. safe_free(response);
  38. if (nonce)
  39. authDigestNonceUnlink(nonce);
  40. }
  41. int
  42. AuthDigestUserRequest::authenticated() const
  43. {
  44. if (user() != NULL && user()->credentials() == Auth::Ok)
  45. return 1;
  46. return 0;
  47. }
  48. /** log a digest user in
  49. */
  50. void
  51. AuthDigestUserRequest::authenticate(HttpRequest * request, ConnStateData * conn, http_hdr_type type)
  52. {
  53. HASHHEX SESSIONKEY;
  54. HASHHEX HA2 = "";
  55. HASHHEX Response;
  56. /* if the check has corrupted the user, just return */
  57. if (user() == NULL || user()->credentials() == Auth::Failed) {
  58. return;
  59. }
  60. Auth::User::Pointer auth_user = user();
  61. Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User*>(auth_user.getRaw());
  62. assert(digest_user != NULL);
  63. AuthDigestUserRequest *digest_request = this;
  64. /* do we have the HA1 */
  65. if (!digest_user->HA1created) {
  66. auth_user->credentials(Auth::Pending);
  67. return;
  68. }
  69. if (digest_request->nonce == NULL) {
  70. /* this isn't a nonce we issued */
  71. auth_user->credentials(Auth::Failed);
  72. return;
  73. }
  74. DigestCalcHA1(digest_request->algorithm, NULL, NULL, NULL,
  75. authenticateDigestNonceNonceb64(digest_request->nonce),
  76. digest_request->cnonce,
  77. digest_user->HA1, SESSIONKEY);
  78. DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
  79. digest_request->nc, digest_request->cnonce, digest_request->qop,
  80. RequestMethodStr(request->method), digest_request->uri, HA2, Response);
  81. debugs(29, 9, "\nResponse = '" << digest_request->response << "'\nsquid is = '" << Response << "'");
  82. if (strcasecmp(digest_request->response, Response) != 0) {
  83. if (!digest_request->flags.helper_queried) {
  84. /* Query the helper in case the password has changed */
  85. digest_request->flags.helper_queried = 1;
  86. auth_user->credentials(Auth::Pending);
  87. return;
  88. }
  89. if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->PostWorkaround && request->method != METHOD_GET) {
  90. /* Ugly workaround for certain very broken browsers using the
  91. * wrong method to calculate the request-digest on POST request.
  92. * This should be deleted once Digest authentication becomes more
  93. * widespread and such broken browsers no longer are commonly
  94. * used.
  95. */
  96. DigestCalcResponse(SESSIONKEY, authenticateDigestNonceNonceb64(digest_request->nonce),
  97. digest_request->nc, digest_request->cnonce, digest_request->qop,
  98. RequestMethodStr(METHOD_GET), digest_request->uri, HA2, Response);
  99. if (strcasecmp(digest_request->response, Response)) {
  100. auth_user->credentials(Auth::Failed);
  101. digest_request->flags.invalid_password = 1;
  102. digest_request->setDenyMessage("Incorrect password");
  103. return;
  104. } else {
  105. const char *useragent = request->header.getStr(HDR_USER_AGENT);
  106. static Ip::Address last_broken_addr;
  107. static int seen_broken_client = 0;
  108. if (!seen_broken_client) {
  109. last_broken_addr.SetNoAddr();
  110. seen_broken_client = 1;
  111. }
  112. if (last_broken_addr != request->client_addr) {
  113. debugs(29, 1, "\nDigest POST bug detected from " <<
  114. request->client_addr << " using '" <<
  115. (useragent ? useragent : "-") <<
  116. "'. Please upgrade browser. See Bug #630 for details.");
  117. last_broken_addr = request->client_addr;
  118. }
  119. }
  120. } else {
  121. auth_user->credentials(Auth::Failed);
  122. digest_request->flags.invalid_password = 1;
  123. digest_request->setDenyMessage("Incorrect password");
  124. return;
  125. }
  126. /* check for stale nonce */
  127. if (!authDigestNonceIsValid(digest_request->nonce, digest_request->nc)) {
  128. debugs(29, 3, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK but nonce stale");
  129. auth_user->credentials(Auth::Failed);
  130. digest_request->setDenyMessage("Stale nonce");
  131. return;
  132. }
  133. }
  134. auth_user->credentials(Auth::Ok);
  135. /* password was checked and did match */
  136. debugs(29, 4, "authenticateDigestAuthenticateuser: user '" << auth_user->username() << "' validated OK");
  137. /* auth_user is now linked, we reset these values
  138. * after external auth occurs anyway */
  139. auth_user->expiretime = current_time.tv_sec;
  140. return;
  141. }
  142. Auth::Direction
  143. AuthDigestUserRequest::module_direction()
  144. {
  145. if (user()->auth_type != Auth::AUTH_DIGEST)
  146. return Auth::CRED_ERROR;
  147. switch (user()->credentials()) {
  148. case Auth::Ok:
  149. return Auth::CRED_VALID;
  150. case Auth::Failed:
  151. /* send new challenge */
  152. return Auth::CRED_CHALLENGE;
  153. case Auth::Unchecked:
  154. case Auth::Pending:
  155. return Auth::CRED_LOOKUP;
  156. default:
  157. return Auth::CRED_ERROR;
  158. }
  159. }
  160. void
  161. AuthDigestUserRequest::addAuthenticationInfoHeader(HttpReply * rep, int accel)
  162. {
  163. http_hdr_type type;
  164. /* don't add to authentication error pages */
  165. if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
  166. || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
  167. return;
  168. type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
  169. #if WAITING_FOR_TE
  170. /* test for http/1.1 transfer chunked encoding */
  171. if (chunkedtest)
  172. return;
  173. #endif
  174. if ((static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram) && authDigestNonceLastRequest(nonce)) {
  175. flags.authinfo_sent = 1;
  176. debugs(29, 9, "authDigestAddHead: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
  177. httpHeaderPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
  178. }
  179. }
  180. #if WAITING_FOR_TE
  181. void
  182. AuthDigestUserRequest::addAuthenticationInfoTrailer(HttpReply * rep, int accel)
  183. {
  184. int type;
  185. if (!auth_user_request)
  186. return;
  187. /* has the header already been send? */
  188. if (flags.authinfo_sent)
  189. return;
  190. /* don't add to authentication error pages */
  191. if ((!accel && rep->sline.status == HTTP_PROXY_AUTHENTICATION_REQUIRED)
  192. || (accel && rep->sline.status == HTTP_UNAUTHORIZED))
  193. return;
  194. type = accel ? HDR_AUTHENTICATION_INFO : HDR_PROXY_AUTHENTICATION_INFO;
  195. if ((static_cast<Auth::Digest::Config*>(digestScheme::GetInstance()->getConfig())->authenticate) && authDigestNonceLastRequest(nonce)) {
  196. debugs(29, 9, "authDigestAddTrailer: Sending type:" << type << " header: 'nextnonce=\"" << authenticateDigestNonceNonceb64(nonce) << "\"");
  197. httpTrailerPutStrf(&rep->header, type, "nextnonce=\"%s\"", authenticateDigestNonceNonceb64(nonce));
  198. }
  199. }
  200. #endif
  201. /* send the initial data to a digest authenticator module */
  202. void
  203. AuthDigestUserRequest::module_start(RH * handler, void *data)
  204. {
  205. char buf[8192];
  206. assert(user() != NULL && user()->auth_type == Auth::AUTH_DIGEST);
  207. debugs(29, 9, "authenticateStart: '\"" << user()->username() << "\":\"" << realm << "\"'");
  208. if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->authenticateProgram == NULL) {
  209. debugs(29, DBG_CRITICAL, "ERROR: No Digest authentication program configured.");
  210. handler(data, NULL);
  211. return;
  212. }
  213. if (static_cast<Auth::Digest::Config*>(Auth::Config::Find("digest"))->utf8) {
  214. char userstr[1024];
  215. latin1_to_utf8(userstr, sizeof(userstr), user()->username());
  216. snprintf(buf, 8192, "\"%s\":\"%s\"\n", userstr, realm);
  217. } else {
  218. snprintf(buf, 8192, "\"%s\":\"%s\"\n", user()->username(), realm);
  219. }
  220. helperSubmit(digestauthenticators, buf, AuthDigestUserRequest::HandleReply,
  221. new Auth::StateData(this, handler, data));
  222. }
  223. void
  224. AuthDigestUserRequest::HandleReply(void *data, char *reply)
  225. {
  226. Auth::StateData *replyData = static_cast<Auth::StateData *>(data);
  227. char *t = NULL;
  228. void *cbdata;
  229. debugs(29, 9, HERE << "{" << (reply ? reply : "<NULL>") << "}");
  230. if (reply) {
  231. if ((t = strchr(reply, ' ')))
  232. *t++ = '\0';
  233. if (*reply == '\0' || *reply == '\n')
  234. reply = NULL;
  235. }
  236. assert(replyData->auth_user_request != NULL);
  237. AuthUserRequest::Pointer auth_user_request = replyData->auth_user_request;
  238. if (reply && (strncasecmp(reply, "ERR", 3) == 0)) {
  239. /* allow this because the digest_request pointer is purely local */
  240. AuthDigestUserRequest *digest_request = dynamic_cast<AuthDigestUserRequest *>(auth_user_request.getRaw());
  241. assert(digest_request);
  242. digest_request->user()->credentials(Auth::Failed);
  243. digest_request->flags.invalid_password = 1;
  244. if (t && *t)
  245. digest_request->setDenyMessage(t);
  246. } else if (reply) {
  247. /* allow this because the digest_request pointer is purely local */
  248. Auth::Digest::User *digest_user = dynamic_cast<Auth::Digest::User *>(auth_user_request->user().getRaw());
  249. assert(digest_user != NULL);
  250. CvtBin(reply, digest_user->HA1);
  251. digest_user->HA1created = 1;
  252. }
  253. if (cbdataReferenceValidDone(replyData->data, &cbdata))
  254. replyData->handler(cbdata, NULL);
  255. delete replyData;
  256. }