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
19namespace net {
20
21HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
22
23// static
24void 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
57HttpAuth::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
90HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs()
91    const {
92  return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
93}
94
95std::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
108void 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(&params_begin_, &params_end_);
126}
127
128// static
129std::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
142std::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
155std::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
168const 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