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
Identity()21 HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
22
23 // static
ChooseBestChallenge(HttpAuthHandlerFactory * http_auth_handler_factory,const HttpResponseHeaders * headers,Target target,const GURL & origin,const std::set<Scheme> & disabled_schemes,const BoundNetLog & net_log,scoped_ptr<HttpAuthHandler> * handler)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
HandleChallengeResponse(HttpAuthHandler * handler,const HttpResponseHeaders * headers,Target target,const std::set<Scheme> & disabled_schemes,std::string * challenge_used)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
param_pairs() const90 HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs()
91 const {
92 return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
93 }
94
base64_param() const95 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
Init(std::string::const_iterator begin,std::string::const_iterator end)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
GetChallengeHeaderName(Target target)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
GetAuthorizationHeaderName(Target target)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
GetAuthTargetString(Target target)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
SchemeToString(Scheme scheme)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