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 17 namespace net { 18 19 namespace { 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. 37 bool 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 56 bool HttpAuthHandlerBasic::Init(HttpAuthChallengeTokenizer* challenge) { 57 auth_scheme_ = HttpAuth::AUTH_SCHEME_BASIC; 58 score_ = 1; 59 properties_ = 0; 60 return ParseChallenge(challenge); 61 } 62 63 bool 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 77 HttpAuth::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 90 int 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 103 HttpAuthHandlerBasic::Factory::Factory() { 104 } 105 106 HttpAuthHandlerBasic::Factory::~Factory() { 107 } 108 109 int 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