15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2011 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_auth_handler_ntlm.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if !defined(NTLM_SSPI)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
117d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "base/strings/string_util.h"
12868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_errors.h"
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/net_util.h"
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "net/http/http_auth_challenge_tokenizer.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::HandleAnotherChallenge(
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HttpAuthChallengeTokenizer* challenge) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ParseChallenge(challenge, false);
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool HttpAuthHandlerNTLM::Init(HttpAuthChallengeTokenizer* tok) {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auth_scheme_ = HttpAuth::AUTH_SCHEME_NTLM;
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  score_ = 3;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  properties_ = ENCRYPTS_IDENTITY | IS_CONNECTION_BASED;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return ParseChallenge(tok, true) == HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int HttpAuthHandlerNTLM::GenerateAuthTokenImpl(
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const AuthCredentials* credentials, const HttpRequestInfo* request,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const CompletionCallback& callback, std::string* auth_token) {
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(NTLM_SSPI)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return auth_sspi_.GenerateAuthToken(
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      credentials,
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateSPN(origin_),
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      auth_token);
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else  // !defined(NTLM_SSPI)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(cbentzel): Shouldn't be hitting this case.
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!credentials) {
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "Username and password are expected to be non-NULL.";
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ERR_MISSING_AUTH_CREDENTIALS;
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(wtc): See if we can use char* instead of void* for in_buf and
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // out_buf.  This change will need to propagate to GetNextToken,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // GenerateType1Msg, and GenerateType3Msg, and perhaps further.
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const void* in_buf;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void* out_buf;
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uint32 in_buf_len, out_buf_len;
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string decoded_auth_data;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The username may be in the form "DOMAIN\user".  Parse it into the two
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // components.
56c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::string16 domain;
57c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  base::string16 user;
58c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const base::string16& username = credentials->username();
597d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  const base::char16 backslash_character = '\\';
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  size_t backslash_idx = username.find(backslash_character);
61c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (backslash_idx == base::string16::npos) {
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user = username;
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    domain = username.substr(0, backslash_idx);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    user = username.substr(backslash_idx + 1);
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  domain_ = domain;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  credentials_.Set(user, credentials->password());
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Initial challenge.
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (auth_data_.empty()) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    in_buf_len = 0;
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    in_buf = NULL;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int rv = InitializeBeforeFirstChallenge();
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (rv != OK)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return rv;
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!base::Base64Decode(auth_data_, &decoded_auth_data)) {
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      LOG(ERROR) << "Unexpected problem Base64 decoding.";
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return ERR_UNEXPECTED;
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    in_buf_len = decoded_auth_data.length();
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    in_buf = decoded_auth_data.data();
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int rv = GetNextToken(in_buf, in_buf_len, &out_buf, &out_buf_len);
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (rv != OK)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return rv;
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Base64 encode data in output buffer and prepend "NTLM ".
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string encode_output;
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  base::Base64Encode(encode_input, &encode_output);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // OK, we are done with |out_buf|
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  free(out_buf);
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *auth_token = std::string("NTLM ") + encode_output;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return OK;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The NTLM challenge header looks like:
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   WWW-Authenticate: NTLM auth-data
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge(
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    HttpAuthChallengeTokenizer* tok, bool initial_challenge) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(NTLM_SSPI)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // auth_sspi_ contains state for whether or not this is the initial challenge.
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return auth_sspi_.ParseChallenge(tok);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#else
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(cbentzel): Most of the logic between SSPI, GSSAPI, and portable NTLM
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // authentication parsing could probably be shared - just need to know if
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // there was previously a challenge round.
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // TODO(cbentzel): Write a test case to validate that auth_data_ is left empty
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in all failure conditions.
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auth_data_.clear();
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Verify the challenge's auth-scheme.
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!LowerCaseEqualsASCII(tok->scheme(), "ntlm"))
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return HttpAuth::AUTHORIZATION_RESULT_INVALID;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string base64_param = tok->base64_param();
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (base64_param.empty()) {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!initial_challenge)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return HttpAuth::AUTHORIZATION_RESULT_REJECT;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  } else {
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (initial_challenge)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return HttpAuth::AUTHORIZATION_RESULT_INVALID;
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  auth_data_ = base64_param;
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // defined(NTLM_SSPI)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// static
136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)std::string HttpAuthHandlerNTLM::CreateSPN(const GURL& origin) {
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The service principal name of the destination server.  See
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // http://msdn.microsoft.com/en-us/library/ms677949%28VS.85%29.aspx
139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  std::string target("HTTP/");
140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  target.append(GetHostAndPort(origin));
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return target;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
145