Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2011 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/strings/string_tokenizer.h"
     11 #include "base/strings/string_util.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/http/http_auth_handler.h"
     14 #include "net/http/http_auth_handler_factory.h"
     15 #include "net/http/http_request_headers.h"
     16 #include "net/http/http_response_headers.h"
     17 #include "net/http/http_util.h"
     18 
     19 namespace net {
     20 
     21 HttpAuth::Identity::Identity() : source(IDENT_SRC_NONE), invalid(true) {}
     22 
     23 // static
     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
     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 
     90 HttpAuth::ChallengeTokenizer::ChallengeTokenizer(
     91     std::string::const_iterator begin,
     92     std::string::const_iterator end)
     93     : begin_(begin),
     94       end_(end),
     95       scheme_begin_(begin),
     96       scheme_end_(begin),
     97       params_begin_(end),
     98       params_end_(end) {
     99   Init(begin, end);
    100 }
    101 
    102 HttpUtil::NameValuePairsIterator HttpAuth::ChallengeTokenizer::param_pairs()
    103     const {
    104   return HttpUtil::NameValuePairsIterator(params_begin_, params_end_, ',');
    105 }
    106 
    107 std::string HttpAuth::ChallengeTokenizer::base64_param() const {
    108   // Strip off any padding.
    109   // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
    110   //
    111   // Our base64 decoder requires that the length be a multiple of 4.
    112   int encoded_length = params_end_ - params_begin_;
    113   while (encoded_length > 0 && encoded_length % 4 != 0 &&
    114          params_begin_[encoded_length - 1] == '=') {
    115     --encoded_length;
    116   }
    117   return std::string(params_begin_, params_begin_ + encoded_length);
    118 }
    119 
    120 void HttpAuth::ChallengeTokenizer::Init(std::string::const_iterator begin,
    121                                         std::string::const_iterator end) {
    122   // The first space-separated token is the auth-scheme.
    123   // NOTE: we are more permissive than RFC 2617 which says auth-scheme
    124   // is separated by 1*SP.
    125   base::StringTokenizer tok(begin, end, HTTP_LWS);
    126   if (!tok.GetNext()) {
    127     // Default param and scheme iterators provide empty strings
    128     return;
    129   }
    130 
    131   // Save the scheme's position.
    132   scheme_begin_ = tok.token_begin();
    133   scheme_end_ = tok.token_end();
    134 
    135   params_begin_ = scheme_end_;
    136   params_end_ = end;
    137   HttpUtil::TrimLWS(&params_begin_, &params_end_);
    138 }
    139 
    140 // static
    141 std::string HttpAuth::GetChallengeHeaderName(Target target) {
    142   switch (target) {
    143     case AUTH_PROXY:
    144       return "Proxy-Authenticate";
    145     case AUTH_SERVER:
    146       return "WWW-Authenticate";
    147     default:
    148       NOTREACHED();
    149       return std::string();
    150   }
    151 }
    152 
    153 // static
    154 std::string HttpAuth::GetAuthorizationHeaderName(Target target) {
    155   switch (target) {
    156     case AUTH_PROXY:
    157       return HttpRequestHeaders::kProxyAuthorization;
    158     case AUTH_SERVER:
    159       return HttpRequestHeaders::kAuthorization;
    160     default:
    161       NOTREACHED();
    162       return std::string();
    163   }
    164 }
    165 
    166 // static
    167 std::string HttpAuth::GetAuthTargetString(Target target) {
    168   switch (target) {
    169     case AUTH_PROXY:
    170       return "proxy";
    171     case AUTH_SERVER:
    172       return "server";
    173     default:
    174       NOTREACHED();
    175       return std::string();
    176   }
    177 }
    178 
    179 // static
    180 const char* HttpAuth::SchemeToString(Scheme scheme) {
    181   static const char* const kSchemeNames[] = {
    182     "basic",
    183     "digest",
    184     "ntlm",
    185     "negotiate",
    186     "spdyproxy",
    187     "mock",
    188   };
    189   COMPILE_ASSERT(arraysize(kSchemeNames) == AUTH_SCHEME_MAX,
    190                  http_auth_scheme_names_incorrect_size);
    191   if (scheme < AUTH_SCHEME_BASIC || scheme >= AUTH_SCHEME_MAX) {
    192     NOTREACHED();
    193     return "invalid_scheme";
    194   }
    195   return kSchemeNames[scheme];
    196 }
    197 
    198 }  // namespace net
    199