1c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be
3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file.
4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
5c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth.h"
6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <algorithm>
8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/basictypes.h"
10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/string_util.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/net_errors.h"
12c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_basic.h"
13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_digest.h"
14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_negotiate.h"
15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_auth_handler_ntlm.h"
16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_response_headers.h"
17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "net/http/http_util.h"
18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottnamespace net {
20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
21731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickHttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
22731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid HttpAuth::ChooseBestChallenge(
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    HttpAuthHandlerFactory* http_auth_handler_factory,
26c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const HttpResponseHeaders* headers,
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Target target,
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const GURL& origin,
2972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::set<Scheme>& disabled_schemes,
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const BoundNetLog& net_log,
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scoped_ptr<HttpAuthHandler>* handler) {
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(http_auth_handler_factory);
333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(handler->get() == NULL);
34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Choose the challenge whose authentication handler gives the maximum score.
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<HttpAuthHandler> best;
37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  const std::string header_name = GetChallengeHeaderName(target);
38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  std::string cur_challenge;
39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  void* iter = NULL;
40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  while (headers->EnumerateHeader(&iter, header_name, &cur_challenge)) {
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    scoped_ptr<HttpAuthHandler> cur;
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int rv = http_auth_handler_factory->CreateAuthHandlerFromString(
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        cur_challenge, target, origin, net_log, &cur);
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (rv != OK) {
45731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      VLOG(1) << "Unable to create AuthHandler. Status: "
46731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick              << ErrorToString(rv) << " Challenge: " << cur_challenge;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (cur.get() && (!best.get() || best->score() < cur->score()) &&
5072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        (disabled_schemes.find(cur->auth_scheme()) == disabled_schemes.end()))
513345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      best.swap(cur);
52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  handler->swap(best);
54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// static
573345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickHttpAuth::AuthorizationResult HttpAuth::HandleChallengeResponse(
583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    HttpAuthHandler* handler,
593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    const HttpResponseHeaders* headers,
603345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    Target target,
6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::set<Scheme>& disabled_schemes,
623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    std::string* challenge_used) {
63731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(handler);
64731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  DCHECK(headers);
653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  DCHECK(challenge_used);
66731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  challenge_used->clear();
6772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  HttpAuth::Scheme current_scheme = handler->auth_scheme();
683345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (disabled_schemes.find(current_scheme) != disabled_schemes.end())
693345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return HttpAuth::AUTHORIZATION_RESULT_REJECT;
7072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  std::string current_scheme_name = SchemeToString(current_scheme);
713345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  const std::string header_name = GetChallengeHeaderName(target);
723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  void* iter = NULL;
733345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::string challenge;
743345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  HttpAuth::AuthorizationResult authorization_result =
753345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      HttpAuth::AUTHORIZATION_RESULT_INVALID;
763345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  while (headers->EnumerateHeader(&iter, header_name, &challenge)) {
773345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    HttpAuth::ChallengeTokenizer props(challenge.begin(), challenge.end());
7872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    if (!LowerCaseEqualsASCII(props.scheme(), current_scheme_name.c_str()))
793345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      continue;
803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    authorization_result = handler->HandleAnotherChallenge(&props);
813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    if (authorization_result != HttpAuth::AUTHORIZATION_RESULT_INVALID) {
823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      *challenge_used = challenge;
833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      return authorization_result;
843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    }
853345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Finding no matches is equivalent to rejection.
873345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return HttpAuth::AUTHORIZATION_RESULT_REJECT;
883345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
893345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
9072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenHttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs()
9172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const {
9272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
9372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
9472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
9572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuth::ChallengeTokenizer::base64_param() const {
9672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Strip off any padding.
9772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
9872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  //
9972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  // Our base64 decoder requires that the length be a multiple of 4.
10072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  int encoded_length = params_end_ - params_begin_;
10172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  while (encoded_length > 0 && encoded_length % 4 != 0 &&
10272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen         params_begin_[encoded_length - 1] == '=') {
10372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    --encoded_length;
10472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
10572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return std::string(params_begin_, params_begin_ + encoded_length);
10672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
10772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin,
109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott                                        std::string::const_iterator end) {
110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // The first space-separated token is the auth-scheme.
111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // NOTE: we are more permissive than RFC 2617 which says auth-scheme
112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // is separated by 1*SP.
113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  StringTokenizer tok(begin, end, HTTP_LWS);
114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  if (!tok.GetNext()) {
115731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    // Default param and scheme iterators provide empty strings
116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    return;
117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  // Save the scheme's position.
120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  scheme_begin_ = tok.token_begin();
121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  scheme_end_ = tok.token_end();
122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
123731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  params_begin_ = scheme_end_;
124731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  params_end_ = end;
125731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  HttpUtil::TrimLWS(&params_begin_, &params_end_);
126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
128c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
129c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpAuth::GetChallengeHeaderName(Target target) {
130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  switch (target) {
131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case AUTH_PROXY:
132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "Proxy-Authenticate";
133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case AUTH_SERVER:
134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "WWW-Authenticate";
135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    default:
136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      NOTREACHED();
137c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "";
138c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
139c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
140c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// static
142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottstd::string HttpAuth::GetAuthorizationHeaderName(Target target) {
143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  switch (target) {
144c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case AUTH_PROXY:
145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "Proxy-Authorization";
146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    case AUTH_SERVER:
147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "Authorization";
148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott    default:
149c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      NOTREACHED();
150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott      return "";
151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott  }
152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}
153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
15572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenstd::string HttpAuth::GetAuthTargetString(Target target) {
15672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  switch (target) {
15772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case AUTH_PROXY:
15872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "proxy";
15972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    case AUTH_SERVER:
16072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "server";
16172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    default:
16272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      NOTREACHED();
16372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      return "";
16472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
16572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen}
16672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
16772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// static
16872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst char* HttpAuth::SchemeToString(Scheme scheme) {
16972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  static const char* const kSchemeNames[] = {
17072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    "basic",
17172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    "digest",
17272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    "ntlm",
17372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    "negotiate",
17472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    "mock",
17572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  };
17672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX,
17772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                 http_auth_scheme_names_incorrect_size);
17872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) {
17972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    NOTREACHED();
18072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return "invalid_scheme";
18172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  }
18272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return kSchemeNames[scheme];
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
185c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott}  // namespace net
186