1 // Copyright (c) 2006-2008 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/http/http_auth_handler_basic.h" 12 #include "net/http/http_auth_handler_digest.h" 13 #include "net/http/http_auth_handler_negotiate.h" 14 #include "net/http/http_auth_handler_ntlm.h" 15 #include "net/http/http_response_headers.h" 16 #include "net/http/http_util.h" 17 18 namespace net { 19 20 // static 21 void HttpAuth::ChooseBestChallenge(const HttpResponseHeaders* headers, 22 Target target, 23 const GURL& origin, 24 scoped_refptr<HttpAuthHandler>* handler) { 25 // A connection-based authentication scheme must continue to use the 26 // existing handler object in |*handler|. 27 if (*handler && (*handler)->is_connection_based()) { 28 const std::string header_name = GetChallengeHeaderName(target); 29 std::string challenge; 30 void* iter = NULL; 31 while (headers->EnumerateHeader(&iter, header_name, &challenge)) { 32 ChallengeTokenizer props(challenge.begin(), challenge.end()); 33 if (LowerCaseEqualsASCII(props.scheme(), (*handler)->scheme().c_str()) && 34 (*handler)->InitFromChallenge(challenge.begin(), challenge.end(), 35 target, origin)) 36 return; 37 } 38 } 39 40 // Choose the challenge whose authentication handler gives the maximum score. 41 scoped_refptr<HttpAuthHandler> best; 42 const std::string header_name = GetChallengeHeaderName(target); 43 std::string cur_challenge; 44 void* iter = NULL; 45 while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) { 46 scoped_refptr<HttpAuthHandler> cur; 47 CreateAuthHandler(cur_challenge, target, origin, &cur); 48 if (cur && (!best || best->score() < cur->score())) 49 best.swap(cur); 50 } 51 handler->swap(best); 52 } 53 54 // static 55 void HttpAuth::CreateAuthHandler(const std::string& challenge, 56 Target target, 57 const GURL& origin, 58 scoped_refptr<HttpAuthHandler>* handler) { 59 // Find the right auth handler for the challenge's scheme. 60 ChallengeTokenizer props(challenge.begin(), challenge.end()); 61 if (!props.valid()) { 62 *handler = NULL; 63 return; 64 } 65 66 scoped_refptr<HttpAuthHandler> tmp_handler; 67 if (LowerCaseEqualsASCII(props.scheme(), "basic")) { 68 tmp_handler = new HttpAuthHandlerBasic(); 69 } else if (LowerCaseEqualsASCII(props.scheme(), "digest")) { 70 tmp_handler = new HttpAuthHandlerDigest(); 71 } else if (LowerCaseEqualsASCII(props.scheme(), "negotiate")) { 72 tmp_handler = new HttpAuthHandlerNegotiate(); 73 } else if (LowerCaseEqualsASCII(props.scheme(), "ntlm")) { 74 tmp_handler = new HttpAuthHandlerNTLM(); 75 } 76 if (tmp_handler) { 77 if (!tmp_handler->InitFromChallenge(challenge.begin(), challenge.end(), 78 target, origin)) { 79 // Invalid/unsupported challenge. 80 tmp_handler = NULL; 81 } 82 } 83 handler->swap(tmp_handler); 84 } 85 86 void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin, 87 std::string::const_iterator end) { 88 // The first space-separated token is the auth-scheme. 89 // NOTE: we are more permissive than RFC 2617 which says auth-scheme 90 // is separated by 1*SP. 91 StringTokenizer tok(begin, end, HTTP_LWS); 92 if (!tok.GetNext()) { 93 valid_ = false; 94 return; 95 } 96 97 // Save the scheme's position. 98 scheme_begin_ = tok.token_begin(); 99 scheme_end_ = tok.token_end(); 100 101 // Everything past scheme_end_ is a (comma separated) value list. 102 props_ = HttpUtil::ValuesIterator(scheme_end_, end, ','); 103 } 104 105 // We expect properties to be formatted as one of: 106 // name="value" 107 // name=value 108 // name= 109 bool HttpAuth::ChallengeTokenizer::GetNext() { 110 if (!props_.GetNext()) 111 return false; 112 113 // Set the value as everything. Next we will split out the name. 114 value_begin_ = props_.value_begin(); 115 value_end_ = props_.value_end(); 116 name_begin_ = name_end_ = value_end_; 117 118 // Scan for the equals sign. 119 std::string::const_iterator equals = std::find(value_begin_, value_end_, '='); 120 if (equals == value_end_ || equals == value_begin_) 121 return valid_ = false; // Malformed 122 123 // Verify that the equals sign we found wasn't inside of quote marks. 124 for (std::string::const_iterator it = value_begin_; it != equals; ++it) { 125 if (HttpUtil::IsQuote(*it)) 126 return valid_ = false; // Malformed 127 } 128 129 name_begin_ = value_begin_; 130 name_end_ = equals; 131 value_begin_ = equals + 1; 132 133 if (value_begin_ != value_end_ && HttpUtil::IsQuote(*value_begin_)) { 134 // Trim surrounding quotemarks off the value 135 if (*value_begin_ != *(value_end_ - 1)) 136 return valid_ = false; // Malformed -- mismatching quotes. 137 value_is_quoted_ = true; 138 } else { 139 value_is_quoted_ = false; 140 } 141 return true; 142 } 143 144 // If value() has quotemarks, unquote it. 145 std::string HttpAuth::ChallengeTokenizer::unquoted_value() const { 146 return HttpUtil::Unquote(value_begin_, value_end_); 147 } 148 149 // static 150 std::string HttpAuth::GetChallengeHeaderName(Target target) { 151 switch (target) { 152 case AUTH_PROXY: 153 return "Proxy-Authenticate"; 154 case AUTH_SERVER: 155 return "WWW-Authenticate"; 156 default: 157 NOTREACHED(); 158 return ""; 159 } 160 } 161 162 // static 163 std::string HttpAuth::GetAuthorizationHeaderName(Target target) { 164 switch (target) { 165 case AUTH_PROXY: 166 return "Proxy-Authorization"; 167 case AUTH_SERVER: 168 return "Authorization"; 169 default: 170 NOTREACHED(); 171 return ""; 172 } 173 } 174 175 } // namespace net 176