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