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
ChooseBestChallenge(const HttpResponseHeaders * headers,Target target,const GURL & origin,scoped_refptr<HttpAuthHandler> * handler)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
CreateAuthHandler(const std::string & challenge,Target target,const GURL & origin,scoped_refptr<HttpAuthHandler> * handler)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
Init(std::string::const_iterator begin,std::string::const_iterator end)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=
GetNext()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.
unquoted_value() const145 std::string HttpAuth::ChallengeTokenizer::unquoted_value() const {
146 return HttpUtil::Unquote(value_begin_, value_end_);
147 }
148
149 // static
GetChallengeHeaderName(Target target)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
GetAuthorizationHeaderName(Target target)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