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