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.h" 6 7#include <algorithm> 8 9#include "base/basictypes.h" 10#include "base/strings/string_tokenizer.h" 11#include "base/strings/string_util.h" 12#include "net/base/net_errors.h" 13#include "net/http/http_auth_handler.h" 14#include "net/http/http_auth_handler_factory.h" 15#include "net/http/http_request_headers.h" 16#include "net/http/http_response_headers.h" 17#include "net/http/http_util.h" 18 19namespace net { 20 21HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {} 22 23// static 24void HttpAuth::ChooseBestChallenge( 25 HttpAuthHandlerFactory* http_auth_handler_factory, 26 const HttpResponseHeaders* headers, 27 Target target, 28 const GURL& origin, 29 const std::set<Scheme>& disabled_schemes, 30 const BoundNetLog& net_log, 31 scoped_ptr<HttpAuthHandler>* handler) { 32 DCHECK(http_auth_handler_factory); 33 DCHECK(handler->get() == NULL); 34 35 // Choose the challenge whose authentication handler gives the maximum score. 36 scoped_ptr<HttpAuthHandler> best; 37 const std::string header_name = GetChallengeHeaderName(target); 38 std::string cur_challenge; 39 void* iter = NULL; 40 while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) { 41 scoped_ptr<HttpAuthHandler> cur; 42 int rv = http_auth_handler_factory->CreateAuthHandlerFromString( 43 cur_challenge, target, origin, net_log, &cur); 44 if (rv != OK) { 45 VLOG(1) << "Unable to create AuthHandler. Status: " 46 << ErrorToString(rv) << " Challenge: " << cur_challenge; 47 continue; 48 } 49 if (cur.get() && (!best.get() || best->score() < cur->score()) && 50 (disabled_schemes.find(cur->auth_scheme()) == disabled_schemes.end())) 51 best.swap(cur); 52 } 53 handler->swap(best); 54} 55 56// static 57HttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse( 58 HttpAuthHandler* handler, 59 const HttpResponseHeaders* headers, 60 Target target, 61 const std::set<Scheme>& disabled_schemes, 62 std::string* challenge_used) { 63 DCHECK(handler); 64 DCHECK(headers); 65 DCHECK(challenge_used); 66 challenge_used->clear(); 67 HttpAuth::Scheme current_scheme = handler->auth_scheme(); 68 if (disabled_schemes.find(current_scheme) != disabled_schemes.end()) 69 return HttpAuth::AUTHORIZATION_RESULT_REJECT; 70 std::string current_scheme_name = SchemeToString(current_scheme); 71 const std::string header_name = GetChallengeHeaderName(target); 72 void* iter = NULL; 73 std::string challenge; 74 HttpAuth::AuthorizationResult authorization_result = 75 HttpAuth::AUTHORIZATION_RESULT_INVALID; 76 while (headers->EnumerateHeader(&iter, header_name, &challenge)) { 77 HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end()); 78 if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str())) 79 continue; 80 authorization_result = handler->HandleAnotherChallenge(&props); 81 if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) { 82 *challenge_used = challenge; 83 return authorization_result; 84 } 85 } 86 // Finding no matches is equivalent to rejection. 87 return HttpAuth::AUTHORIZATION_RESULT_REJECT; 88} 89 90HttpAuth::ChallengeTokenizer::ChallengeTokenizer( 91 std::string::const_iterator begin, 92 std::string::const_iterator end) 93 : begin_(begin), 94 end_(end), 95 scheme_begin_(begin), 96 scheme_end_(begin), 97 params_begin_(end), 98 params_end_(end) { 99 Init(begin, end); 100} 101 102HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs() 103 const { 104 return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ','); 105} 106 107std::string HttpAuth::ChallengeTokenizer::base64_param() const { 108 // Strip off any padding. 109 // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.) 110 // 111 // Our base64 decoder requires that the length be a multiple of 4. 112 int encoded_length = params_end_ - params_begin_; 113 while (encoded_length > 0 && encoded_length % 4 != 0 && 114 params_begin_[encoded_length - 1] == '=') { 115 --encoded_length; 116 } 117 return std::string(params_begin_, params_begin_ + encoded_length); 118} 119 120void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, 121 std::string::const_iterator end) { 122 // The first space-separated token is the auth-scheme. 123 // NOTE: we are more permissive than RFC 2617 which says auth-scheme 124 // is separated by 1*SP. 125 base::StringTokenizer tok(begin, end, HTTP_LWS); 126 if (!tok.GetNext()) { 127 // Default param and scheme iterators provide empty strings 128 return; 129 } 130 131 // Save the scheme's position. 132 scheme_begin_ = tok.token_begin(); 133 scheme_end_ = tok.token_end(); 134 135 params_begin_ = scheme_end_; 136 params_end_ = end; 137 HttpUtil::TrimLWS(¶ms_begin_, ¶ms_end_); 138} 139 140// static 141std::string HttpAuth::GetChallengeHeaderName(Target target) { 142 switch (target) { 143 case AUTH_PROXY: 144 return "Proxy-Authenticate"; 145 case AUTH_SERVER: 146 return "WWW-Authenticate"; 147 default: 148 NOTREACHED(); 149 return std::string(); 150 } 151} 152 153// static 154std::string HttpAuth::GetAuthorizationHeaderName(Target target) { 155 switch (target) { 156 case AUTH_PROXY: 157 return HttpRequestHeaders::kProxyAuthorization; 158 case AUTH_SERVER: 159 return HttpRequestHeaders::kAuthorization; 160 default: 161 NOTREACHED(); 162 return std::string(); 163 } 164} 165 166// static 167std::string HttpAuth::GetAuthTargetString(Target target) { 168 switch (target) { 169 case AUTH_PROXY: 170 return "proxy"; 171 case AUTH_SERVER: 172 return "server"; 173 default: 174 NOTREACHED(); 175 return std::string(); 176 } 177} 178 179// static 180const char* HttpAuth::SchemeToString(Scheme scheme) { 181 static const char* const kSchemeNames[] = { 182 "basic", 183 "digest", 184 "ntlm", 185 "negotiate", 186 "spdyproxy", 187 "mock", 188 }; 189 COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX, 190 http_auth_scheme_names_incorrect_size); 191 if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) { 192 NOTREACHED(); 193 return "invalid_scheme"; 194 } 195 return kSchemeNames[scheme]; 196} 197 198} // namespace net 199