blob: 37022cd4b2b86c3f20132a09b81be51e7dafdbe3 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/http/http_auth_handler_ntlm.h"
#include "base/base64.h"
#include "base/metrics/histogram_macros.h"
#include "base/rand_util.h"
#include "base/time/time.h"
#include "net/base/net_errors.h"
#include "net/base/network_interfaces.h"
#include "net/dns/host_resolver.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_challenge_tokenizer.h"
#include "net/http/http_auth_handler_ntlm.h"
#include "net/http/http_auth_multi_round_parse.h"
#include "net/http/http_auth_preferences.h"
#include "net/http/http_auth_scheme.h"
#include "net/ssl/ssl_info.h"
namespace net {
namespace {
uint64_t GetMSTime() {
return base::Time::Now().since_origin().InMicroseconds() * 10;
}
void GenerateRandom(uint8_t* output, size_t n) {
base::RandBytes(output, n);
}
} // namespace
int HttpAuthHandlerNTLM::Factory::CreateAuthHandler(
HttpAuthChallengeTokenizer* challenge,
HttpAuth::Target target,
const SSLInfo& ssl_info,
const GURL& origin,
CreateReason reason,
int digest_nonce_count,
const NetLogWithSource& net_log,
HostResolver* host_resolver,
std::unique_ptr<HttpAuthHandler>* handler) {
if (reason == CREATE_PREEMPTIVE)
return ERR_UNSUPPORTED_AUTH_SCHEME;
// TODO(cbentzel): Move towards model of parsing in the factory
// method and only constructing when valid.
// NOTE: Default credentials are not supported for the portable implementation
// of NTLM.
std::unique_ptr<HttpAuthHandler> tmp_handler(
new HttpAuthHandlerNTLM(http_auth_preferences()));
if (!tmp_handler->InitFromChallenge(challenge, target, ssl_info, origin,
net_log))
return ERR_INVALID_RESPONSE;
handler->swap(tmp_handler);
return OK;
}
// static
HttpAuthHandlerNTLM::GetMSTimeProc HttpAuthHandlerNTLM::get_ms_time_proc_ =
GetMSTime;
// static
HttpAuthHandlerNTLM::GenerateRandomProc
HttpAuthHandlerNTLM::generate_random_proc_ = GenerateRandom;
// static
HttpAuthHandlerNTLM::HostNameProc HttpAuthHandlerNTLM::get_host_name_proc_ =
GetHostName;
HttpAuthHandlerNTLM::HttpAuthHandlerNTLM(
const HttpAuthPreferences* http_auth_preferences)
: ntlm_client_(ntlm::NtlmFeatures(
http_auth_preferences ? http_auth_preferences->NtlmV2Enabled()
: true)) {}
bool HttpAuthHandlerNTLM::NeedsIdentity() {
// This gets called for each round-trip. Only require identity on the first
// call (when challenge_token_ is empty). On subsequent calls, we use the
// initially established identity.
return challenge_token_.empty();
}
bool HttpAuthHandlerNTLM::AllowsDefaultCredentials() {
// Default credentials are not supported in the portable implementation of
// NTLM, but are supported in the SSPI implementation.
return false;
}
int HttpAuthHandlerNTLM::GenerateAuthTokenImpl(
const AuthCredentials* credentials,
const HttpRequestInfo* request,
CompletionOnceCallback callback,
std::string* auth_token) {
// TODO(cbentzel): Shouldn't be hitting this case.
if (!credentials) {
LOG(ERROR) << "Username and password are expected to be non-nullptr.";
return ERR_MISSING_AUTH_CREDENTIALS;
}
// The username may be in the form "DOMAIN\user". Parse it into the two
// components.
base::string16 domain;
base::string16 user;
const base::string16& username = credentials->username();
const base::char16 backslash_character = '\\';
size_t backslash_idx = username.find(backslash_character);
if (backslash_idx == base::string16::npos) {
user = username;
} else {
domain = username.substr(0, backslash_idx);
user = username.substr(backslash_idx + 1);
}
domain_ = domain;
credentials_.Set(user, credentials->password());
std::vector<uint8_t> next_token =
GetNextToken(base::as_bytes(base::make_span(challenge_token_)));
if (next_token.empty())
return ERR_UNEXPECTED;
// Base64 encode data in output buffer and prepend "NTLM ".
std::string encode_output;
base::Base64Encode(
base::StringPiece(reinterpret_cast<const char*>(next_token.data()),
next_token.size()),
&encode_output);
*auth_token = std::string("NTLM ") + encode_output;
return OK;
}
HttpAuthHandlerNTLM::~HttpAuthHandlerNTLM() = default;
// static
HttpAuthHandlerNTLM::GetMSTimeProc HttpAuthHandlerNTLM::SetGetMSTimeProc(
GetMSTimeProc proc) {
GetMSTimeProc old_proc = get_ms_time_proc_;
get_ms_time_proc_ = proc;
return old_proc;
}
// static
HttpAuthHandlerNTLM::GenerateRandomProc
HttpAuthHandlerNTLM::SetGenerateRandomProc(GenerateRandomProc proc) {
GenerateRandomProc old_proc = generate_random_proc_;
generate_random_proc_ = proc;
return old_proc;
}
// static
HttpAuthHandlerNTLM::HostNameProc HttpAuthHandlerNTLM::SetHostNameProc(
HostNameProc proc) {
HostNameProc old_proc = get_host_name_proc_;
get_host_name_proc_ = proc;
return old_proc;
}
std::vector<uint8_t> HttpAuthHandlerNTLM::GetNextToken(
base::span<const uint8_t> in_token) {
// If in_token is non-empty, then assume it contains a challenge message,
// and generate the Authenticate message in reply. Otherwise return the
// Negotiate message.
if (in_token.empty()) {
return ntlm_client_.GetNegotiateMessage();
}
std::string hostname = get_host_name_proc_();
if (hostname.empty())
return {};
uint8_t client_challenge[8];
generate_random_proc_(client_challenge, 8);
uint64_t client_time = get_ms_time_proc_();
return ntlm_client_.GenerateAuthenticateMessage(
domain_, credentials_.username(), credentials_.password(), hostname,
channel_bindings_, CreateSPN(origin_), client_time, client_challenge,
in_token);
}
HttpAuth::AuthorizationResult HttpAuthHandlerNTLM::ParseChallenge(
HttpAuthChallengeTokenizer* tok,
bool initial_challenge) {
challenge_token_.clear();
if (initial_challenge)
return ParseFirstRoundChallenge(HttpAuth::Scheme::AUTH_SCHEME_NTLM, tok);
std::string encoded_token;
return ParseLaterRoundChallenge(HttpAuth::Scheme::AUTH_SCHEME_NTLM, tok,
&encoded_token, &challenge_token_);
}
} // namespace net