PageRenderTime 45ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/net/http/http_auth_handler_digest.cc

https://gitlab.com/jonnialva90/iridium-browser
C++ | 383 lines | 256 code | 49 blank | 78 comment | 50 complexity | 5e5d4c5ef704c31522d3da52d8bbc272 MD5 | raw file
  1. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/http/http_auth_handler_digest.h"
  5. #include <string>
  6. #include "base/logging.h"
  7. #include "base/md5.h"
  8. #include "base/rand_util.h"
  9. #include "base/strings/string_util.h"
  10. #include "base/strings/stringprintf.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "net/base/net_errors.h"
  13. #include "net/base/net_string_util.h"
  14. #include "net/base/net_util.h"
  15. #include "net/http/http_auth.h"
  16. #include "net/http/http_auth_challenge_tokenizer.h"
  17. #include "net/http/http_request_info.h"
  18. #include "net/http/http_util.h"
  19. #include "url/gurl.h"
  20. namespace net {
  21. // Digest authentication is specified in RFC 2617.
  22. // The expanded derivations are listed in the tables below.
  23. //==========+==========+==========================================+
  24. // qop |algorithm | response |
  25. //==========+==========+==========================================+
  26. // ? | ?, md5, | MD5(MD5(A1):nonce:MD5(A2)) |
  27. // | md5-sess | |
  28. //--------- +----------+------------------------------------------+
  29. // auth, | ?, md5, | MD5(MD5(A1):nonce:nc:cnonce:qop:MD5(A2)) |
  30. // auth-int | md5-sess | |
  31. //==========+==========+==========================================+
  32. // qop |algorithm | A1 |
  33. //==========+==========+==========================================+
  34. // | ?, md5 | user:realm:password |
  35. //----------+----------+------------------------------------------+
  36. // | md5-sess | MD5(user:realm:password):nonce:cnonce |
  37. //==========+==========+==========================================+
  38. // qop |algorithm | A2 |
  39. //==========+==========+==========================================+
  40. // ?, auth | | req-method:req-uri |
  41. //----------+----------+------------------------------------------+
  42. // auth-int | | req-method:req-uri:MD5(req-entity-body) |
  43. //=====================+==========================================+
  44. HttpAuthHandlerDigest::NonceGenerator::NonceGenerator() {
  45. }
  46. HttpAuthHandlerDigest::NonceGenerator::~NonceGenerator() {
  47. }
  48. HttpAuthHandlerDigest::DynamicNonceGenerator::DynamicNonceGenerator() {
  49. }
  50. std::string HttpAuthHandlerDigest::DynamicNonceGenerator::GenerateNonce()
  51. const {
  52. // This is how mozilla generates their cnonce -- a 16 digit hex string.
  53. static const char domain[] = "0123456789abcdef";
  54. std::string cnonce;
  55. cnonce.reserve(16);
  56. for (int i = 0; i < 16; ++i)
  57. cnonce.push_back(domain[base::RandInt(0, 15)]);
  58. return cnonce;
  59. }
  60. HttpAuthHandlerDigest::FixedNonceGenerator::FixedNonceGenerator(
  61. const std::string& nonce)
  62. : nonce_(nonce) {
  63. }
  64. std::string HttpAuthHandlerDigest::FixedNonceGenerator::GenerateNonce() const {
  65. return nonce_;
  66. }
  67. HttpAuthHandlerDigest::Factory::Factory()
  68. : nonce_generator_(new DynamicNonceGenerator()) {
  69. }
  70. HttpAuthHandlerDigest::Factory::~Factory() {
  71. }
  72. void HttpAuthHandlerDigest::Factory::set_nonce_generator(
  73. const NonceGenerator* nonce_generator) {
  74. nonce_generator_.reset(nonce_generator);
  75. }
  76. int HttpAuthHandlerDigest::Factory::CreateAuthHandler(
  77. HttpAuthChallengeTokenizer* challenge,
  78. HttpAuth::Target target,
  79. const GURL& origin,
  80. CreateReason reason,
  81. int digest_nonce_count,
  82. const BoundNetLog& net_log,
  83. scoped_ptr<HttpAuthHandler>* handler) {
  84. // TODO(cbentzel): Move towards model of parsing in the factory
  85. // method and only constructing when valid.
  86. scoped_ptr<HttpAuthHandler> tmp_handler(
  87. new HttpAuthHandlerDigest(digest_nonce_count, nonce_generator_.get()));
  88. if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
  89. return ERR_INVALID_RESPONSE;
  90. handler->swap(tmp_handler);
  91. return OK;
  92. }
  93. HttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge(
  94. HttpAuthChallengeTokenizer* challenge) {
  95. // Even though Digest is not connection based, a "second round" is parsed
  96. // to differentiate between stale and rejected responses.
  97. // Note that the state of the current handler is not mutated - this way if
  98. // there is a rejection the realm hasn't changed.
  99. if (!base::LowerCaseEqualsASCII(challenge->scheme(), "digest"))
  100. return HttpAuth::AUTHORIZATION_RESULT_INVALID;
  101. HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs();
  102. // Try to find the "stale" value, and also keep track of the realm
  103. // for the new challenge.
  104. std::string original_realm;
  105. while (parameters.GetNext()) {
  106. if (base::LowerCaseEqualsASCII(parameters.name(), "stale")) {
  107. if (base::LowerCaseEqualsASCII(parameters.value(), "true"))
  108. return HttpAuth::AUTHORIZATION_RESULT_STALE;
  109. } else if (base::LowerCaseEqualsASCII(parameters.name(), "realm")) {
  110. original_realm = parameters.value();
  111. }
  112. }
  113. return (original_realm_ != original_realm) ?
  114. HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM :
  115. HttpAuth::AUTHORIZATION_RESULT_REJECT;
  116. }
  117. bool HttpAuthHandlerDigest::Init(HttpAuthChallengeTokenizer* challenge) {
  118. return ParseChallenge(challenge);
  119. }
  120. int HttpAuthHandlerDigest::GenerateAuthTokenImpl(
  121. const AuthCredentials* credentials, const HttpRequestInfo* request,
  122. const CompletionCallback& callback, std::string* auth_token) {
  123. // Generate a random client nonce.
  124. std::string cnonce = nonce_generator_->GenerateNonce();
  125. // Extract the request method and path -- the meaning of 'path' is overloaded
  126. // in certain cases, to be a hostname.
  127. std::string method;
  128. std::string path;
  129. GetRequestMethodAndPath(request, &method, &path);
  130. *auth_token = AssembleCredentials(method, path, *credentials,
  131. cnonce, nonce_count_);
  132. return OK;
  133. }
  134. HttpAuthHandlerDigest::HttpAuthHandlerDigest(
  135. int nonce_count, const NonceGenerator* nonce_generator)
  136. : stale_(false),
  137. algorithm_(ALGORITHM_UNSPECIFIED),
  138. qop_(QOP_UNSPECIFIED),
  139. nonce_count_(nonce_count),
  140. nonce_generator_(nonce_generator) {
  141. DCHECK(nonce_generator_);
  142. }
  143. HttpAuthHandlerDigest::~HttpAuthHandlerDigest() {
  144. }
  145. // The digest challenge header looks like:
  146. // WWW-Authenticate: Digest
  147. // [realm="<realm-value>"]
  148. // nonce="<nonce-value>"
  149. // [domain="<list-of-URIs>"]
  150. // [opaque="<opaque-token-value>"]
  151. // [stale="<true-or-false>"]
  152. // [algorithm="<digest-algorithm>"]
  153. // [qop="<list-of-qop-values>"]
  154. // [<extension-directive>]
  155. //
  156. // Note that according to RFC 2617 (section 1.2) the realm is required.
  157. // However we allow it to be omitted, in which case it will default to the
  158. // empty string.
  159. //
  160. // This allowance is for better compatibility with webservers that fail to
  161. // send the realm (See http://crbug.com/20984 for an instance where a
  162. // webserver was not sending the realm with a BASIC challenge).
  163. bool HttpAuthHandlerDigest::ParseChallenge(
  164. HttpAuthChallengeTokenizer* challenge) {
  165. auth_scheme_ = HttpAuth::AUTH_SCHEME_DIGEST;
  166. score_ = 2;
  167. properties_ = ENCRYPTS_IDENTITY;
  168. // Initialize to defaults.
  169. stale_ = false;
  170. algorithm_ = ALGORITHM_UNSPECIFIED;
  171. qop_ = QOP_UNSPECIFIED;
  172. realm_ = original_realm_ = nonce_ = domain_ = opaque_ = std::string();
  173. // FAIL -- Couldn't match auth-scheme.
  174. if (!base::LowerCaseEqualsASCII(challenge->scheme(), "digest"))
  175. return false;
  176. HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs();
  177. // Loop through all the properties.
  178. while (parameters.GetNext()) {
  179. // FAIL -- couldn't parse a property.
  180. if (!ParseChallengeProperty(parameters.name(),
  181. parameters.value()))
  182. return false;
  183. }
  184. // Check if tokenizer failed.
  185. if (!parameters.valid())
  186. return false;
  187. // Check that a minimum set of properties were provided.
  188. if (nonce_.empty())
  189. return false;
  190. return true;
  191. }
  192. bool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name,
  193. const std::string& value) {
  194. if (base::LowerCaseEqualsASCII(name, "realm")) {
  195. std::string realm;
  196. if (!ConvertToUtf8AndNormalize(value, kCharsetLatin1, &realm))
  197. return false;
  198. realm_ = realm;
  199. original_realm_ = value;
  200. } else if (base::LowerCaseEqualsASCII(name, "nonce")) {
  201. nonce_ = value;
  202. } else if (base::LowerCaseEqualsASCII(name, "domain")) {
  203. domain_ = value;
  204. } else if (base::LowerCaseEqualsASCII(name, "opaque")) {
  205. opaque_ = value;
  206. } else if (base::LowerCaseEqualsASCII(name, "stale")) {
  207. // Parse the stale boolean.
  208. stale_ = base::LowerCaseEqualsASCII(value, "true");
  209. } else if (base::LowerCaseEqualsASCII(name, "algorithm")) {
  210. // Parse the algorithm.
  211. if (base::LowerCaseEqualsASCII(value, "md5")) {
  212. algorithm_ = ALGORITHM_MD5;
  213. } else if (base::LowerCaseEqualsASCII(value, "md5-sess")) {
  214. algorithm_ = ALGORITHM_MD5_SESS;
  215. } else {
  216. DVLOG(1) << "Unknown value of algorithm";
  217. return false; // FAIL -- unsupported value of algorithm.
  218. }
  219. } else if (base::LowerCaseEqualsASCII(name, "qop")) {
  220. // Parse the comma separated list of qops.
  221. // auth is the only supported qop, and all other values are ignored.
  222. HttpUtil::ValuesIterator qop_values(value.begin(), value.end(), ',');
  223. qop_ = QOP_UNSPECIFIED;
  224. while (qop_values.GetNext()) {
  225. if (base::LowerCaseEqualsASCII(qop_values.value(), "auth")) {
  226. qop_ = QOP_AUTH;
  227. break;
  228. }
  229. }
  230. } else {
  231. DVLOG(1) << "Skipping unrecognized digest property";
  232. // TODO(eroman): perhaps we should fail instead of silently skipping?
  233. }
  234. return true;
  235. }
  236. // static
  237. std::string HttpAuthHandlerDigest::QopToString(QualityOfProtection qop) {
  238. switch (qop) {
  239. case QOP_UNSPECIFIED:
  240. return std::string();
  241. case QOP_AUTH:
  242. return "auth";
  243. default:
  244. NOTREACHED();
  245. return std::string();
  246. }
  247. }
  248. // static
  249. std::string HttpAuthHandlerDigest::AlgorithmToString(
  250. DigestAlgorithm algorithm) {
  251. switch (algorithm) {
  252. case ALGORITHM_UNSPECIFIED:
  253. return std::string();
  254. case ALGORITHM_MD5:
  255. return "MD5";
  256. case ALGORITHM_MD5_SESS:
  257. return "MD5-sess";
  258. default:
  259. NOTREACHED();
  260. return std::string();
  261. }
  262. }
  263. void HttpAuthHandlerDigest::GetRequestMethodAndPath(
  264. const HttpRequestInfo* request,
  265. std::string* method,
  266. std::string* path) const {
  267. DCHECK(request);
  268. const GURL& url = request->url;
  269. if (target_ == HttpAuth::AUTH_PROXY &&
  270. (url.SchemeIs("https") || url.SchemeIsWSOrWSS())) {
  271. *method = "CONNECT";
  272. *path = GetHostAndPort(url);
  273. } else {
  274. *method = request->method;
  275. *path = url.PathForRequest();
  276. }
  277. }
  278. std::string HttpAuthHandlerDigest::AssembleResponseDigest(
  279. const std::string& method,
  280. const std::string& path,
  281. const AuthCredentials& credentials,
  282. const std::string& cnonce,
  283. const std::string& nc) const {
  284. // ha1 = MD5(A1)
  285. // TODO(eroman): is this the right encoding?
  286. std::string ha1 = base::MD5String(base::UTF16ToUTF8(credentials.username()) +
  287. ":" + original_realm_ + ":" +
  288. base::UTF16ToUTF8(credentials.password()));
  289. if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS)
  290. ha1 = base::MD5String(ha1 + ":" + nonce_ + ":" + cnonce);
  291. // ha2 = MD5(A2)
  292. // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int.
  293. std::string ha2 = base::MD5String(method + ":" + path);
  294. std::string nc_part;
  295. if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) {
  296. nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":";
  297. }
  298. return base::MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2);
  299. }
  300. std::string HttpAuthHandlerDigest::AssembleCredentials(
  301. const std::string& method,
  302. const std::string& path,
  303. const AuthCredentials& credentials,
  304. const std::string& cnonce,
  305. int nonce_count) const {
  306. // the nonce-count is an 8 digit hex string.
  307. std::string nc = base::StringPrintf("%08x", nonce_count);
  308. // TODO(eroman): is this the right encoding?
  309. std::string authorization = (std::string("Digest username=") +
  310. HttpUtil::Quote(
  311. base::UTF16ToUTF8(credentials.username())));
  312. authorization += ", realm=" + HttpUtil::Quote(original_realm_);
  313. authorization += ", nonce=" + HttpUtil::Quote(nonce_);
  314. authorization += ", uri=" + HttpUtil::Quote(path);
  315. if (algorithm_ != ALGORITHM_UNSPECIFIED) {
  316. authorization += ", algorithm=" + AlgorithmToString(algorithm_);
  317. }
  318. std::string response = AssembleResponseDigest(method, path, credentials,
  319. cnonce, nc);
  320. // No need to call HttpUtil::Quote() as the response digest cannot contain
  321. // any characters needing to be escaped.
  322. authorization += ", response=\"" + response + "\"";
  323. if (!opaque_.empty()) {
  324. authorization += ", opaque=" + HttpUtil::Quote(opaque_);
  325. }
  326. if (qop_ != QOP_UNSPECIFIED) {
  327. // TODO(eroman): Supposedly IIS server requires quotes surrounding qop.
  328. authorization += ", qop=" + QopToString(qop_);
  329. authorization += ", nc=" + nc;
  330. authorization += ", cnonce=" + HttpUtil::Quote(cnonce);
  331. }
  332. return authorization;
  333. }
  334. } // namespace net