Home | History | Annotate | Download | only in http
      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
     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
     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 
     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=
    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.
    145 std::string HttpAuth::ChallengeTokenizer::unquoted_value() const {
    146   return HttpUtil::Unquote(value_begin_, value_end_);
    147 }
    148 
    149 // static
    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
    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