• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2019 The Chromium Authors
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_ntlm_mechanism.h"
6 
7 #include "base/base64.h"
8 #include "base/containers/span.h"
9 #include "base/logging.h"
10 #include "base/rand_util.h"
11 #include "base/time/time.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/network_interfaces.h"
14 #include "net/http/http_auth_challenge_tokenizer.h"
15 #include "net/http/http_auth_multi_round_parse.h"
16 #include "net/http/http_auth_preferences.h"
17 #include "net/http/http_auth_scheme.h"
18 
19 namespace net {
20 
21 namespace {
22 
GetMSTime()23 uint64_t GetMSTime() {
24   return base::Time::Now().since_origin().InMicroseconds() * 10;
25 }
26 
GenerateRandom(uint8_t * output,size_t n)27 void GenerateRandom(uint8_t* output, size_t n) {
28   base::RandBytes(output, n);
29 }
30 
31 // static
32 HttpAuthNtlmMechanism::GetMSTimeProc g_get_ms_time_proc = GetMSTime;
33 
34 // static
35 HttpAuthNtlmMechanism::GenerateRandomProc g_generate_random_proc =
36     GenerateRandom;
37 
38 // static
39 HttpAuthNtlmMechanism::HostNameProc g_host_name_proc = GetHostName;
40 
41 template <typename T>
SwapOut(T * target,T source)42 T SwapOut(T* target, T source) {
43   T t = *target;
44   *target = source;
45   return t;
46 }
47 
SetAuthTokenFromBinaryToken(std::string * auth_token,const std::vector<uint8_t> & next_token)48 int SetAuthTokenFromBinaryToken(std::string* auth_token,
49                                 const std::vector<uint8_t>& next_token) {
50   if (next_token.empty())
51     return ERR_UNEXPECTED;
52 
53   std::string encode_output;
54   base::Base64Encode(
55       base::StringPiece(reinterpret_cast<const char*>(next_token.data()),
56                         next_token.size()),
57       &encode_output);
58 
59   *auth_token = std::string("NTLM ") + encode_output;
60   return OK;
61 }
62 
63 }  // namespace
64 
ScopedProcSetter(GetMSTimeProc ms_time_proc,GenerateRandomProc random_proc,HostNameProc host_name_proc)65 HttpAuthNtlmMechanism::ScopedProcSetter::ScopedProcSetter(
66     GetMSTimeProc ms_time_proc,
67     GenerateRandomProc random_proc,
68     HostNameProc host_name_proc) {
69   old_ms_time_proc_ = SwapOut(&g_get_ms_time_proc, ms_time_proc);
70   old_random_proc_ = SwapOut(&g_generate_random_proc, random_proc);
71   old_host_name_proc_ = SwapOut(&g_host_name_proc, host_name_proc);
72 }
73 
~ScopedProcSetter()74 HttpAuthNtlmMechanism::ScopedProcSetter::~ScopedProcSetter() {
75   g_get_ms_time_proc = old_ms_time_proc_;
76   g_generate_random_proc = old_random_proc_;
77   g_host_name_proc = old_host_name_proc_;
78 }
79 
HttpAuthNtlmMechanism(const HttpAuthPreferences * http_auth_preferences)80 HttpAuthNtlmMechanism::HttpAuthNtlmMechanism(
81     const HttpAuthPreferences* http_auth_preferences)
82     : ntlm_client_(ntlm::NtlmFeatures(
83           http_auth_preferences ? http_auth_preferences->NtlmV2Enabled()
84                                 : true)) {}
85 
86 HttpAuthNtlmMechanism::~HttpAuthNtlmMechanism() = default;
87 
Init(const NetLogWithSource & net_log)88 bool HttpAuthNtlmMechanism::Init(const NetLogWithSource& net_log) {
89   return true;
90 }
91 
NeedsIdentity() const92 bool HttpAuthNtlmMechanism::NeedsIdentity() const {
93   // This gets called for each round-trip. Only require identity on the first
94   // call (when challenge_token_ is empty). On subsequent calls, we use the
95   // initially established identity.
96   return challenge_token_.empty();
97 }
98 
AllowsExplicitCredentials() const99 bool HttpAuthNtlmMechanism::AllowsExplicitCredentials() const {
100   return true;
101 }
102 
ParseChallenge(HttpAuthChallengeTokenizer * tok)103 HttpAuth::AuthorizationResult HttpAuthNtlmMechanism::ParseChallenge(
104     HttpAuthChallengeTokenizer* tok) {
105   if (!first_token_sent_)
106     return ParseFirstRoundChallenge(HttpAuth::Scheme::AUTH_SCHEME_NTLM, tok);
107 
108   challenge_token_.clear();
109   std::string encoded_token;
110   return ParseLaterRoundChallenge(HttpAuth::Scheme::AUTH_SCHEME_NTLM, tok,
111                                   &encoded_token, &challenge_token_);
112 }
113 
GenerateAuthToken(const AuthCredentials * credentials,const std::string & spn,const std::string & channel_bindings,std::string * auth_token,const NetLogWithSource & net_log,CompletionOnceCallback callback)114 int HttpAuthNtlmMechanism::GenerateAuthToken(
115     const AuthCredentials* credentials,
116     const std::string& spn,
117     const std::string& channel_bindings,
118     std::string* auth_token,
119     const NetLogWithSource& net_log,
120     CompletionOnceCallback callback) {
121   if (!credentials) {
122     LOG(ERROR) << "Username and password are expected to be non-nullptr.";
123     return ERR_MISSING_AUTH_CREDENTIALS;
124   }
125 
126   if (challenge_token_.empty()) {
127     if (first_token_sent_)
128       return ERR_UNEXPECTED;
129     first_token_sent_ = true;
130     return SetAuthTokenFromBinaryToken(auth_token,
131                                        ntlm_client_.GetNegotiateMessage());
132   }
133 
134   // The username may be in the form "DOMAIN\user".  Parse it into the two
135   // components.
136   std::u16string domain;
137   std::u16string user;
138   const std::u16string& username = credentials->username();
139   const char16_t backslash_character = '\\';
140   size_t backslash_idx = username.find(backslash_character);
141   if (backslash_idx == std::u16string::npos) {
142     user = username;
143   } else {
144     domain = username.substr(0, backslash_idx);
145     user = username.substr(backslash_idx + 1);
146   }
147 
148   std::string hostname = g_host_name_proc();
149   if (hostname.empty())
150     return ERR_UNEXPECTED;
151 
152   uint8_t client_challenge[8];
153   g_generate_random_proc(client_challenge, 8);
154 
155   auto next_token = ntlm_client_.GenerateAuthenticateMessage(
156       domain, user, credentials->password(), hostname, channel_bindings, spn,
157       g_get_ms_time_proc(), client_challenge,
158       base::as_bytes(base::make_span(challenge_token_)));
159 
160   return SetAuthTokenFromBinaryToken(auth_token, next_token);
161 }
162 
SetDelegation(HttpAuth::DelegationType delegation_type)163 void HttpAuthNtlmMechanism::SetDelegation(
164     HttpAuth::DelegationType delegation_type) {
165   // Nothing to do.
166 }
167 
168 }  // namespace net
169