// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/http/http_auth.h" #include <algorithm> #include "base/basictypes.h" #include "base/string_util.h" #include "net/http/http_auth_handler_basic.h" #include "net/http/http_auth_handler_digest.h" #include "net/http/http_auth_handler_negotiate.h" #include "net/http/http_auth_handler_ntlm.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" namespace net { // static void HttpAuth::ChooseBestChallenge(const HttpResponseHeaders* headers, Target target, const GURL& origin, scoped_refptr<HttpAuthHandler>* handler) { // A connection-based authentication scheme must continue to use the // existing handler object in |*handler|. if (*handler && (*handler)->is_connection_based()) { const std::string header_name = GetChallengeHeaderName(target); std::string challenge; void* iter = NULL; while (headers->EnumerateHeader(&iter, header_name, &challenge)) { ChallengeTokenizer props(challenge.begin(), challenge.end()); if (LowerCaseEqualsASCII(props.scheme(), (*handler)->scheme().c_str()) && (*handler)->InitFromChallenge(challenge.begin(), challenge.end(), target, origin)) return; } } // Choose the challenge whose authentication handler gives the maximum score. scoped_refptr<HttpAuthHandler> best; const std::string header_name = GetChallengeHeaderName(target); std::string cur_challenge; void* iter = NULL; while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) { scoped_refptr<HttpAuthHandler> cur; CreateAuthHandler(cur_challenge, target, origin, &cur); if (cur && (!best || best->score() < cur->score())) best.swap(cur); } handler->swap(best); } // static void HttpAuth::CreateAuthHandler(const std::string& challenge, Target target, const GURL& origin, scoped_refptr<HttpAuthHandler>* handler) { // Find the right auth handler for the challenge's scheme. ChallengeTokenizer props(challenge.begin(), challenge.end()); if (!props.valid()) { *handler = NULL; return; } scoped_refptr<HttpAuthHandler> tmp_handler; if (LowerCaseEqualsASCII(props.scheme(), "basic")) { tmp_handler = new HttpAuthHandlerBasic(); } else if (LowerCaseEqualsASCII(props.scheme(), "digest")) { tmp_handler = new HttpAuthHandlerDigest(); } else if (LowerCaseEqualsASCII(props.scheme(), "negotiate")) { tmp_handler = new HttpAuthHandlerNegotiate(); } else if (LowerCaseEqualsASCII(props.scheme(), "ntlm")) { tmp_handler = new HttpAuthHandlerNTLM(); } if (tmp_handler) { if (!tmp_handler->InitFromChallenge(challenge.begin(), challenge.end(), target, origin)) { // Invalid/unsupported challenge. tmp_handler = NULL; } } handler->swap(tmp_handler); } void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, std::string::const_iterator end) { // The first space-separated token is the auth-scheme. // NOTE: we are more permissive than RFC 2617 which says auth-scheme // is separated by 1*SP. StringTokenizer tok(begin, end, HTTP_LWS); if (!tok.GetNext()) { valid_ = false; return; } // Save the scheme's position. scheme_begin_ = tok.token_begin(); scheme_end_ = tok.token_end(); // Everything past scheme_end_ is a (comma separated) value list. props_ = HttpUtil::ValuesIterator(scheme_end_, end, ','); } // We expect properties to be formatted as one of: // name="value" // name=value // name= bool HttpAuth::ChallengeTokenizer::GetNext() { if (!props_.GetNext()) return false; // Set the value as everything. Next we will split out the name. value_begin_ = props_.value_begin(); value_end_ = props_.value_end(); name_begin_ = name_end_ = value_end_; // Scan for the equals sign. std::string::const_iterator equals = std::find(value_begin_, value_end_, '='); if (equals == value_end_ || equals == value_begin_) return valid_ = false; // Malformed // Verify that the equals sign we found wasn't inside of quote marks. for (std::string::const_iterator it = value_begin_; it != equals; ++it) { if (HttpUtil::IsQuote(*it)) return valid_ = false; // Malformed } name_begin_ = value_begin_; name_end_ = equals; value_begin_ = equals + 1; if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { // Trim surrounding quotemarks off the value if (*value_begin_ != *(value_end_ - 1)) return valid_ = false; // Malformed -- mismatching quotes. value_is_quoted_ = true; } else { value_is_quoted_ = false; } return true; } // If value() has quotemarks, unquote it. std::string HttpAuth::ChallengeTokenizer::unquoted_value() const { return HttpUtil::Unquote(value_begin_, value_end_); } // static std::string HttpAuth::GetChallengeHeaderName(Target target) { switch (target) { case AUTH_PROXY: return "Proxy-Authenticate"; case AUTH_SERVER: return "WWW-Authenticate"; default: NOTREACHED(); return ""; } } // static std::string HttpAuth::GetAuthorizationHeaderName(Target target) { switch (target) { case AUTH_PROXY: return "Proxy-Authorization"; case AUTH_SERVER: return "Authorization"; default: NOTREACHED(); return ""; } } } // namespace net