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 5#include "net/http/http_auth_handler_basic.h" 6 7#include <string> 8 9#include "base/base64.h" 10#include "base/strings/string_util.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/http/http_auth.h" 15#include "net/http/http_auth_challenge_tokenizer.h" 16 17namespace net { 18 19namespace { 20 21// Parses a realm from an auth challenge, and converts to UTF8-encoding. 22// Returns whether the realm is invalid or the parameters are invalid. 23// 24// Note that if a realm was not specified, we will default it to ""; 25// so specifying 'Basic realm=""' is equivalent to 'Basic'. 26// 27// This is more generous than RFC 2617, which is pretty clear in the 28// production of challenge that realm is required. 29// 30// We allow it to be compatibility with certain embedded webservers that don't 31// include a realm (see http://crbug.com/20984.) 32// 33// The over-the-wire realm is encoded as ISO-8859-1 (aka Latin-1). 34// 35// TODO(cbentzel): Realm may need to be decoded using RFC 2047 rules as 36// well, see http://crbug.com/25790. 37bool ParseRealm(const HttpAuthChallengeTokenizer& tokenizer, 38 std::string* realm) { 39 CHECK(realm); 40 realm->clear(); 41 HttpUtil::NameValuePairsIterator parameters = tokenizer.param_pairs(); 42 while (parameters.GetNext()) { 43 if (!LowerCaseEqualsASCII(parameters.name(), "realm")) 44 continue; 45 46 if (!net::ConvertToUtf8AndNormalize(parameters.value(), kCharsetLatin1, 47 realm)) { 48 return false; 49 } 50 } 51 return parameters.valid(); 52} 53 54} // namespace 55 56bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) { 57 auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC; 58 score_ = 1; 59 properties_ = 0; 60 return ParseChallenge(challenge); 61} 62 63bool HttpAuthHandlerBasic::ParseChallenge( 64 HttpAuthChallengeTokenizer* challenge) { 65 // Verify the challenge's auth-scheme. 66 if (!LowerCaseEqualsASCII(challenge->scheme(), "basic")) 67 return false; 68 69 std::string realm; 70 if (!ParseRealm(*challenge, &realm)) 71 return false; 72 73 realm_ = realm; 74 return true; 75} 76 77HttpAuth::AuthorizationResult HttpAuthHandlerBasic::HandleAnotherChallenge( 78 HttpAuthChallengeTokenizer* challenge) { 79 // Basic authentication is always a single round, so any responses 80 // should be treated as a rejection. However, if the new challenge 81 // is for a different realm, then indicate the realm change. 82 std::string realm; 83 if (!ParseRealm(*challenge, &realm)) 84 return HttpAuth::AUTHORIZATION_RESULT_INVALID; 85 return (realm_ != realm)? 86 HttpAuth::AUTHORIZATION_RESULT_DIFFERENT_REALM: 87 HttpAuth::AUTHORIZATION_RESULT_REJECT; 88} 89 90int HttpAuthHandlerBasic::GenerateAuthTokenImpl( 91 const AuthCredentials* credentials, const HttpRequestInfo*, 92 const CompletionCallback&, std::string* auth_token) { 93 DCHECK(credentials); 94 // TODO(eroman): is this the right encoding of username/password? 95 std::string base64_username_password; 96 base::Base64Encode(base::UTF16ToUTF8(credentials->username()) + ":" + 97 base::UTF16ToUTF8(credentials->password()), 98 &base64_username_password); 99 *auth_token = "Basic " + base64_username_password; 100 return OK; 101} 102 103HttpAuthHandlerBasic::Factory::Factory() { 104} 105 106HttpAuthHandlerBasic::Factory::~Factory() { 107} 108 109int HttpAuthHandlerBasic::Factory::CreateAuthHandler( 110 HttpAuthChallengeTokenizer* challenge, 111 HttpAuth::Target target, 112 const GURL& origin, 113 CreateReason reason, 114 int digest_nonce_count, 115 const BoundNetLog& net_log, 116 scoped_ptr<HttpAuthHandler>* handler) { 117 // TODO(cbentzel): Move towards model of parsing in the factory 118 // method and only constructing when valid. 119 scoped_ptr<HttpAuthHandler> tmp_handler(new HttpAuthHandlerBasic()); 120 if (!tmp_handler->InitFromChallenge(challenge, target, origin, net_log)) 121 return ERR_INVALID_RESPONSE; 122 handler->swap(tmp_handler); 123 return OK; 124} 125 126} // namespace net 127