• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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