1 // Copyright (c) 2010 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/string_util.h" 11 #include "net/base/net_errors.h" 12 #include "net/http/http_auth_handler_basic.h" 13 #include "net/http/http_auth_handler_digest.h" 14 #include "net/http/http_auth_handler_negotiate.h" 15 #include "net/http/http_auth_handler_ntlm.h" 16 #include "net/http/http_response_headers.h" 17 #include "net/http/http_util.h" 18 19 namespace net { 20 21 HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {} 22 23 // static 24 void 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 57 HttpAuth::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 90 HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs() 91 const { 92 return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ','); 93 } 94 95 std::string HttpAuth::ChallengeTokenizer::base64_param() const { 96 // Strip off any padding. 97 // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.) 98 // 99 // Our base64 decoder requires that the length be a multiple of 4. 100 int encoded_length = params_end_ - params_begin_; 101 while (encoded_length > 0 && encoded_length % 4 != 0 && 102 params_begin_[encoded_length - 1] == '=') { 103 --encoded_length; 104 } 105 return std::string(params_begin_, params_begin_ + encoded_length); 106 } 107 108 void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, 109 std::string::const_iterator end) { 110 // The first space-separated token is the auth-scheme. 111 // NOTE: we are more permissive than RFC 2617 which says auth-scheme 112 // is separated by 1*SP. 113 StringTokenizer tok(begin, end, HTTP_LWS); 114 if (!tok.GetNext()) { 115 // Default param and scheme iterators provide empty strings 116 return; 117 } 118 119 // Save the scheme's position. 120 scheme_begin_ = tok.token_begin(); 121 scheme_end_ = tok.token_end(); 122 123 params_begin_ = scheme_end_; 124 params_end_ = end; 125 HttpUtil::TrimLWS(¶ms_begin_, ¶ms_end_); 126 } 127 128 // static 129 std::string HttpAuth::GetChallengeHeaderName(Target target) { 130 switch (target) { 131 case AUTH_PROXY: 132 return "Proxy-Authenticate"; 133 case AUTH_SERVER: 134 return "WWW-Authenticate"; 135 default: 136 NOTREACHED(); 137 return ""; 138 } 139 } 140 141 // static 142 std::string HttpAuth::GetAuthorizationHeaderName(Target target) { 143 switch (target) { 144 case AUTH_PROXY: 145 return "Proxy-Authorization"; 146 case AUTH_SERVER: 147 return "Authorization"; 148 default: 149 NOTREACHED(); 150 return ""; 151 } 152 } 153 154 // static 155 std::string HttpAuth::GetAuthTargetString(Target target) { 156 switch (target) { 157 case AUTH_PROXY: 158 return "proxy"; 159 case AUTH_SERVER: 160 return "server"; 161 default: 162 NOTREACHED(); 163 return ""; 164 } 165 } 166 167 // static 168 const char* HttpAuth::SchemeToString(Scheme scheme) { 169 static const char* const kSchemeNames[] = { 170 "basic", 171 "digest", 172 "ntlm", 173 "negotiate", 174 "mock", 175 }; 176 COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX, 177 http_auth_scheme_names_incorrect_size); 178 if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) { 179 NOTREACHED(); 180 return "invalid_scheme"; 181 } 182 return kSchemeNames[scheme]; 183 } 184 185 } // namespace net 186