Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2009 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_handler_ntlm.h"
      6 
      7 #include "base/base64.h"
      8 #include "base/string_util.h"
      9 #include "net/base/net_errors.h"
     10 
     11 namespace net {
     12 
     13 std::string HttpAuthHandlerNTLM::GenerateCredentials(
     14     const std::wstring& username,
     15     const std::wstring& password,
     16     const HttpRequestInfo* request,
     17     const ProxyInfo* proxy) {
     18 #if defined(NTLM_SSPI)
     19   std::string auth_credentials;
     20 
     21   int rv = auth_sspi_.GenerateCredentials(
     22       username,
     23       password,
     24       origin_,
     25       request,
     26       proxy,
     27       &auth_credentials);
     28   if (rv == OK)
     29     return auth_credentials;
     30   return std::string();
     31 #else  // !defined(NTLM_SSPI)
     32   // TODO(wtc): See if we can use char* instead of void* for in_buf and
     33   // out_buf.  This change will need to propagate to GetNextToken,
     34   // GenerateType1Msg, and GenerateType3Msg, and perhaps further.
     35   const void* in_buf;
     36   void* out_buf;
     37   uint32 in_buf_len, out_buf_len;
     38   std::string decoded_auth_data;
     39 
     40   // |username| may be in the form "DOMAIN\user".  Parse it into the two
     41   // components.
     42   std::wstring domain;
     43   std::wstring user;
     44   size_t backslash_idx = username.find(L'\\');
     45   if (backslash_idx == std::wstring::npos) {
     46     user = username;
     47   } else {
     48     domain = username.substr(0, backslash_idx);
     49     user = username.substr(backslash_idx + 1);
     50   }
     51   domain_ = WideToUTF16(domain);
     52   username_ = WideToUTF16(user);
     53   password_ = WideToUTF16(password);
     54 
     55   // Initial challenge.
     56   if (auth_data_.empty()) {
     57     in_buf_len = 0;
     58     in_buf = NULL;
     59     int rv = InitializeBeforeFirstChallenge();
     60     if (rv != OK)
     61       return std::string();
     62   } else {
     63     // Decode |auth_data_| into the input buffer.
     64     int len = auth_data_.length();
     65 
     66     // Strip off any padding.
     67     // (See https://bugzilla.mozilla.org/show_bug.cgi?id=230351.)
     68     //
     69     // Our base64 decoder requires that the length be a multiple of 4.
     70     while (len > 0 && len % 4 != 0 && auth_data_[len - 1] == '=')
     71       len--;
     72     auth_data_.erase(len);
     73 
     74     if (!base::Base64Decode(auth_data_, &decoded_auth_data))
     75       return std::string();  // Improper base64 encoding
     76     in_buf_len = decoded_auth_data.length();
     77     in_buf = decoded_auth_data.data();
     78   }
     79 
     80   int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len);
     81   if (rv != OK)
     82     return std::string();
     83 
     84   // Base64 encode data in output buffer and prepend "NTLM ".
     85   std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
     86   std::string encode_output;
     87   bool ok = base::Base64Encode(encode_input, &encode_output);
     88   // OK, we are done with |out_buf|
     89   free(out_buf);
     90   if (!ok)
     91     return std::string();
     92   return std::string("NTLM ") + encode_output;
     93 #endif
     94 }
     95 
     96 // The NTLM challenge header looks like:
     97 //   WWW-Authenticate: NTLM auth-data
     98 bool HttpAuthHandlerNTLM::ParseChallenge(
     99     std::string::const_iterator challenge_begin,
    100     std::string::const_iterator challenge_end) {
    101   scheme_ = "ntlm";
    102   score_ = 3;
    103   properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
    104 
    105 #if defined(NTLM_SSPI)
    106   return auth_sspi_.ParseChallenge(challenge_begin, challenge_end);
    107 #else
    108   auth_data_.clear();
    109 
    110   // Verify the challenge's auth-scheme.
    111   HttpAuth::ChallengeTokenizer challenge_tok(challenge_begin, challenge_end);
    112   if (!challenge_tok.valid() ||
    113       !LowerCaseEqualsASCII(challenge_tok.scheme(), "ntlm"))
    114     return false;
    115 
    116   // Extract the auth-data.  We can't use challenge_tok.GetNext() because
    117   // auth-data is base64-encoded and may contain '=' padding at the end,
    118   // which would be mistaken for a name=value pair.
    119   challenge_begin += 4;  // Skip over "NTLM".
    120   HttpUtil::TrimLWS(&challenge_begin, &challenge_end);
    121 
    122   auth_data_.assign(challenge_begin, challenge_end);
    123 
    124   return true;
    125 #endif
    126 }
    127 
    128 }  // namespace net
    129