1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_digest.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
73345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include <string>
83345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/md5.h"
11c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/rand_util.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
133345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/stringprintf.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/utf_string_conversions.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_errors.h"
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/base/net_util.h"
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth.h"
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_request_info.h"
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_util.h"
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Digest authentication is specified in RFC 2617.
24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// The expanded derivations are listed in the tables below.
25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//    qop   |algorithm |               response                   |
28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//    ?     |  ?, md5, | MD5(MD5(A1):nonce:MD5(A2))               |
30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//          | md5-sess |                                          |
31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//--------- +----------+------------------------------------------+
32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   auth,  |  ?, md5, | MD5(MD5(A1):nonce:nc:cnonce:qop:MD5(A2)) |
33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// auth-int | md5-sess |                                          |
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//    qop   |algorithm |                  A1                      |
36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//          | ?, md5   | user:realm:password                      |
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//----------+----------+------------------------------------------+
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//          | md5-sess | MD5(user:realm:password):nonce:cnonce    |
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//    qop   |algorithm |                  A2                      |
42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//==========+==========+==========================================+
43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//  ?, auth |          | req-method:req-uri                       |
44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//----------+----------+------------------------------------------+
45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// auth-int |          | req-method:req-uri:MD5(req-entity-body)  |
46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//=====================+==========================================+
47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochHttpAuthHandlerDigest::NonceGenerator::NonceGenerator() {
494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
50c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
514a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochHttpAuthHandlerDigest::NonceGenerator::~NonceGenerator() {
524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochHttpAuthHandlerDigest::DynamicNonceGenerator::DynamicNonceGenerator() {
554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochstd::string HttpAuthHandlerDigest::DynamicNonceGenerator::GenerateNonce()
584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const {
59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // This is how mozilla generates their cnonce -- a 16 digit hex string.
60c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  static const char domain[] = "0123456789abcdef";
61c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string cnonce;
62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  cnonce.reserve(16);
63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  for (int i = 0; i < 16; ++i)
64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    cnonce.push_back(domain[base::RandInt(0, 15)]);
65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return cnonce;
66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochHttpAuthHandlerDigest::FixedNonceGenerator::FixedNonceGenerator(
694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const std::string& nonce)
704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    : nonce_(nonce) {
714a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
724a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochstd::string HttpAuthHandlerDigest::FixedNonceGenerator::GenerateNonce() const {
744a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return nonce_;
754a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
7772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuthHandlerDigest::Factory::Factory()
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : nonce_generator_(new DynamicNonceGenerator()) {
79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuthHandlerDigest::Factory::~Factory() {
82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
8472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid HttpAuthHandlerDigest::Factory::set_nonce_generator(
8572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const NonceGenerator* nonce_generator) {
8672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  nonce_generator_.reset(nonce_generator);
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
8972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenint HttpAuthHandlerDigest::Factory::CreateAuthHandler(
9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    HttpAuth::ChallengeTokenizer* challenge,
9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    HttpAuth::Target target,
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const GURL& origin,
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    CreateReason reason,
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    int digest_nonce_count,
9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const BoundNetLog& net_log,
9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    scoped_ptr<HttpAuthHandler>* handler) {
9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(cbentzel): Move towards model of parsing in the factory
9872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //                 method and only constructing when valid.
9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  scoped_ptr<HttpAuthHandler> tmp_handler(
10072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      new HttpAuthHandlerDigest(digest_nonce_count, nonce_generator_.get()));
10172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log))
10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return ERR_INVALID_RESPONSE;
10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  handler->swap(tmp_handler);
10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return OK;
10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuth::AuthorizationResult HttpAuthHandlerDigest::HandleAnotherChallenge(
10872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    HttpAuth::ChallengeTokenizer* challenge) {
10972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Even though Digest is not connection based, a "second round" is parsed
11072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // to differentiate between stale and rejected responses.
11172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Note that the state of the current handler is not mutated - this way if
11272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // there is a rejection the realm hasn't changed.
11372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!LowerCaseEqualsASCII(challenge->scheme(), "digest"))
11472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return HttpAuth::AUTHORIZATION_RESULT_INVALID;
11572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
11672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs();
117dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  std::string realm;
11872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
119dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // Try to find the "stale" value, and also keep track of the realm
120dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  // for the new challenge.
12172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  while (parameters.GetNext()) {
122dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    if (LowerCaseEqualsASCII(parameters.name(), "stale")) {
123dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      if (LowerCaseEqualsASCII(parameters.value(), "true"))
124dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen        return HttpAuth::AUTHORIZATION_RESULT_STALE;
125dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    } else if (LowerCaseEqualsASCII(parameters.name(), "realm")) {
126dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      realm = parameters.value();
127dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    }
12872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
129dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  return (realm_ != realm) ?
130dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM :
131dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      HttpAuth::AUTHORIZATION_RESULT_REJECT;
13272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
13372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
13472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenbool HttpAuthHandlerDigest::Init(HttpAuth::ChallengeTokenizer* challenge) {
13572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return ParseChallenge(challenge);
1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1373345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint HttpAuthHandlerDigest::GenerateAuthTokenImpl(
1393345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const string16* username,
1403345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const string16* password,
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    const HttpRequestInfo* request,
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    CompletionCallback* callback,
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string* auth_token) {
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Generate a random client nonce.
1454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::string cnonce = nonce_generator_->GenerateNonce();
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Extract the request method and path -- the meaning of 'path' is overloaded
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // in certain cases, to be a hostname.
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string method;
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string path;
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetRequestMethodAndPath(request, &method, &path);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *auth_token = AssembleCredentials(method, path,
1543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    *username,
1553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    *password,
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    cnonce, nonce_count_);
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return OK;
158c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuthHandlerDigest::HttpAuthHandlerDigest(
16172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    int nonce_count, const NonceGenerator* nonce_generator)
16272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : stale_(false),
16372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      algorithm_(ALGORITHM_UNSPECIFIED),
16472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      qop_(QOP_UNSPECIFIED),
16572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      nonce_count_(nonce_count),
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      nonce_generator_(nonce_generator) {
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(nonce_generator_);
1683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpAuthHandlerDigest::~HttpAuthHandlerDigest() {
1713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
173c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// The digest challenge header looks like:
174c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//   WWW-Authenticate: Digest
175c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [realm="<realm-value>"]
176c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     nonce="<nonce-value>"
177c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [domain="<list-of-URIs>"]
178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [opaque="<opaque-token-value>"]
179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [stale="<true-or-false>"]
180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [algorithm="<digest-algorithm>"]
181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [qop="<list-of-qop-values>"]
182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//     [<extension-directive>]
183c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
184c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Note that according to RFC 2617 (section 1.2) the realm is required.
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// However we allow it to be omitted, in which case it will default to the
186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// empty string.
187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott//
188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// This allowance is for better compatibility with webservers that fail to
189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// send the realm (See http://crbug.com/20984 for an instance where a
190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// webserver was not sending the realm with a BASIC challenge).
191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpAuthHandlerDigest::ParseChallenge(
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    HttpAuth::ChallengeTokenizer* challenge) {
19372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  auth_scheme_ = HttpAuth::AUTH_SCHEME_DIGEST;
194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  score_ = 2;
195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  properties_ = ENCRYPTS_IDENTITY;
196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Initialize to defaults.
198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  stale_ = false;
199c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  algorithm_ = ALGORITHM_UNSPECIFIED;
200c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  qop_ = QOP_UNSPECIFIED;
201c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  realm_ = nonce_ = domain_ = opaque_ = std::string();
202c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
2033345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // FAIL -- Couldn't match auth-scheme.
204731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!LowerCaseEqualsASCII(challenge->scheme(), "digest"))
2053345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
206c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
207731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  HttpUtil::NameValuePairsIterator parameters = challenge->param_pairs();
208731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
209c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Loop through all the properties.
210731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  while (parameters.GetNext()) {
2113345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    // FAIL -- couldn't parse a property.
212731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (!ParseChallengeProperty(parameters.name(),
213513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                parameters.value()))
2143345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return false;
215c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
216c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
217c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Check if tokenizer failed.
218731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  if (!parameters.valid())
2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
220c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
221c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Check that a minimum set of properties were provided.
222c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (nonce_.empty())
2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
224c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
225c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
226c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
227c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
228c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool HttpAuthHandlerDigest::ParseChallengeProperty(const std::string& name,
229c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                                   const std::string& value) {
230c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (LowerCaseEqualsASCII(name, "realm")) {
231c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    realm_ = value;
232c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "nonce")) {
233c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    nonce_ = value;
234c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "domain")) {
235c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    domain_ = value;
236c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "opaque")) {
237c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    opaque_ = value;
238c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "stale")) {
239c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Parse the stale boolean.
240c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    stale_ = LowerCaseEqualsASCII(value, "true");
241c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "algorithm")) {
242c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Parse the algorithm.
243c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    if (LowerCaseEqualsASCII(value, "md5")) {
244c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      algorithm_ = ALGORITHM_MD5;
245c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else if (LowerCaseEqualsASCII(value, "md5-sess")) {
246c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      algorithm_ = ALGORITHM_MD5_SESS;
247c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    } else {
248731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      DVLOG(1) << "Unknown value of algorithm";
2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return false;  // FAIL -- unsupported value of algorithm.
250c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
251c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else if (LowerCaseEqualsASCII(name, "qop")) {
252c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // Parse the comma separated list of qops.
2534a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // auth is the only supported qop, and all other values are ignored.
254c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    HttpUtil::ValuesIterator qop_values(value.begin(), value.end(), ',');
2554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    qop_ = QOP_UNSPECIFIED;
256c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    while (qop_values.GetNext()) {
257c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      if (LowerCaseEqualsASCII(qop_values.value(), "auth")) {
2584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        qop_ = QOP_AUTH;
2594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch        break;
260c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      }
261c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    }
262c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  } else {
263731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DVLOG(1) << "Skipping unrecognized digest property";
264c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    // TODO(eroman): perhaps we should fail instead of silently skipping?
265c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
266c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  return true;
267c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
268c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
26972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static
27072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuthHandlerDigest::QopToString(QualityOfProtection qop) {
27172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  switch (qop) {
27272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case QOP_UNSPECIFIED:
27372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "";
27472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case QOP_AUTH:
27572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "auth";
27672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    default:
27772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      NOTREACHED();
27872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "";
27972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
28272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static
28372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuthHandlerDigest::AlgorithmToString(
28472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    DigestAlgorithm algorithm) {
28572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  switch (algorithm) {
28672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ALGORITHM_UNSPECIFIED:
28772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "";
28872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ALGORITHM_MD5:
28972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "MD5";
29072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case ALGORITHM_MD5_SESS:
29172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "MD5-sess";
29272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    default:
29372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      NOTREACHED();
29472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "";
29572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
29872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid HttpAuthHandlerDigest::GetRequestMethodAndPath(
29972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const HttpRequestInfo* request,
30072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    std::string* method,
30172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    std::string* path) const {
30272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  DCHECK(request);
30372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const GURL& url = request->url;
30572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
30672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (target_ == HttpAuth::AUTH_PROXY && url.SchemeIs("https")) {
30772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *method = "CONNECT";
30872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *path = GetHostAndPort(url);
30972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  } else {
31072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *method = request->method;
31172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    *path = HttpUtil::PathForRequest(url);
31272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
3134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
3144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
31572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuthHandlerDigest::AssembleResponseDigest(
31672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& method,
31772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& path,
31872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const string16& username,
31972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const string16& password,
32072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& cnonce,
32172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& nc) const {
32272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // ha1 = MD5(A1)
32372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(eroman): is this the right encoding?
32472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string ha1 = MD5String(UTF16ToUTF8(username) + ":" + realm_ + ":" +
32572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                              UTF16ToUTF8(password));
32672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (algorithm_ == HttpAuthHandlerDigest::ALGORITHM_MD5_SESS)
32772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    ha1 = MD5String(ha1 + ":" + nonce_ + ":" + cnonce);
32872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
32972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // ha2 = MD5(A2)
33072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(eroman): need to add MD5(req-entity-body) for qop=auth-int.
33172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string ha2 = MD5String(method + ":" + path);
33272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
33372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string nc_part;
33472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (qop_ != HttpAuthHandlerDigest::QOP_UNSPECIFIED) {
33572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    nc_part = nc + ":" + cnonce + ":" + QopToString(qop_) + ":";
33672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
33872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return MD5String(ha1 + ":" + nonce_ + ":" + nc_part + ha2);
33972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
34072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
34172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuthHandlerDigest::AssembleCredentials(
34272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& method,
34372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& path,
34472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const string16& username,
34572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const string16& password,
34672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& cnonce,
34772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    int nonce_count) const {
34872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // the nonce-count is an 8 digit hex string.
34972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string nc = base::StringPrintf("%08x", nonce_count);
35072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // TODO(eroman): is this the right encoding?
35272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string authorization = (std::string("Digest username=") +
35372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                               HttpUtil::Quote(UTF16ToUTF8(username)));
35472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  authorization += ", realm=" + HttpUtil::Quote(realm_);
35572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  authorization += ", nonce=" + HttpUtil::Quote(nonce_);
35672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  authorization += ", uri=" + HttpUtil::Quote(path);
35772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
35872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (algorithm_ != ALGORITHM_UNSPECIFIED) {
35972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    authorization += ", algorithm=" + AlgorithmToString(algorithm_);
36072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
36172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string response = AssembleResponseDigest(method, path, username,
36272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                password, cnonce, nc);
36372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // No need to call HttpUtil::Quote() as the response digest cannot contain
36472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // any characters needing to be escaped.
36572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  authorization += ", response=\"" + response + "\"";
36672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
36772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!opaque_.empty()) {
36872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    authorization += ", opaque=" + HttpUtil::Quote(opaque_);
36972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
37072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (qop_ != QOP_UNSPECIFIED) {
37172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // TODO(eroman): Supposedly IIS server requires quotes surrounding qop.
37272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    authorization += ", qop=" + QopToString(qop_);
37372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    authorization += ", nc=" + nc;
37472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    authorization += ", cnonce=" + HttpUtil::Quote(cnonce);
37572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
37772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return authorization;
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
380c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace net
381