xref: /aosp_15_r20/external/cronet/net/http/http_auth_ntlm_mechanism.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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