xref: /aosp_15_r20/external/cronet/net/http/http_auth_sspi_win.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2011 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 // See "SSPI Sample Application" at
6 // http://msdn.microsoft.com/en-us/library/aa918273.aspx
7 
8 #include "net/http/http_auth_sspi_win.h"
9 
10 #include "base/base64.h"
11 #include "base/logging.h"
12 #include "base/notreached.h"
13 #include "base/strings/string_piece.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/values.h"
18 #include "net/base/net_errors.h"
19 #include "net/http/http_auth.h"
20 #include "net/http/http_auth_multi_round_parse.h"
21 #include "net/log/net_log.h"
22 #include "net/log/net_log_event_type.h"
23 #include "net/log/net_log_values.h"
24 #include "net/log/net_log_with_source.h"
25 
26 namespace net {
27 using DelegationType = HttpAuth::DelegationType;
28 
29 namespace {
30 
SecurityStatusToValue(Error mapped_error,SECURITY_STATUS status)31 base::Value::Dict SecurityStatusToValue(Error mapped_error,
32                                         SECURITY_STATUS status) {
33   base::Value::Dict params;
34   params.Set("net_error", mapped_error);
35   params.Set("security_status", static_cast<int>(status));
36   return params;
37 }
38 
AcquireCredentialsHandleParams(const std::u16string * domain,const std::u16string * user,Error result,SECURITY_STATUS status)39 base::Value::Dict AcquireCredentialsHandleParams(const std::u16string* domain,
40                                                  const std::u16string* user,
41                                                  Error result,
42                                                  SECURITY_STATUS status) {
43   base::Value::Dict params;
44   if (domain && user) {
45     params.Set("domain", base::UTF16ToUTF8(*domain));
46     params.Set("user", base::UTF16ToUTF8(*user));
47   }
48   params.Set("status", SecurityStatusToValue(result, status));
49   return params;
50 }
51 
ContextFlagsToValue(DWORD flags)52 base::Value::Dict ContextFlagsToValue(DWORD flags) {
53   base::Value::Dict params;
54   params.Set("value", base::StringPrintf("0x%08lx", flags));
55   params.Set("delegated", (flags & ISC_RET_DELEGATE) == ISC_RET_DELEGATE);
56   params.Set("mutual", (flags & ISC_RET_MUTUAL_AUTH) == ISC_RET_MUTUAL_AUTH);
57   return params;
58 }
59 
ContextAttributesToValue(SSPILibrary * library,PCtxtHandle handle,DWORD attributes)60 base::Value::Dict ContextAttributesToValue(SSPILibrary* library,
61                                            PCtxtHandle handle,
62                                            DWORD attributes) {
63   base::Value::Dict params;
64 
65   SecPkgContext_NativeNames native_names = {0};
66   auto qc_result = library->QueryContextAttributesEx(
67       handle, SECPKG_ATTR_NATIVE_NAMES, &native_names, sizeof(native_names));
68   if (qc_result == SEC_E_OK && native_names.sClientName &&
69       native_names.sServerName) {
70     params.Set("source", base::as_u16cstr(native_names.sClientName));
71     params.Set("target", base::as_u16cstr(native_names.sServerName));
72   }
73 
74   SecPkgContext_NegotiationInfo negotiation_info = {0};
75   qc_result = library->QueryContextAttributesEx(
76       handle, SECPKG_ATTR_NEGOTIATION_INFO, &negotiation_info,
77       sizeof(negotiation_info));
78   if (qc_result == SEC_E_OK && negotiation_info.PackageInfo &&
79       negotiation_info.PackageInfo->Name) {
80     params.Set("mechanism",
81                base::as_u16cstr(negotiation_info.PackageInfo->Name));
82     params.Set("open", negotiation_info.NegotiationState !=
83                            SECPKG_NEGOTIATION_COMPLETE);
84   }
85 
86   SecPkgContext_Authority authority = {0};
87   qc_result = library->QueryContextAttributesEx(handle, SECPKG_ATTR_AUTHORITY,
88                                                 &authority, sizeof(authority));
89   if (qc_result == SEC_E_OK && authority.sAuthorityName) {
90     params.Set("authority", base::as_u16cstr(authority.sAuthorityName));
91   }
92 
93   params.Set("flags", ContextFlagsToValue(attributes));
94   return params;
95 }
96 
InitializeSecurityContextParams(SSPILibrary * library,PCtxtHandle handle,Error result,SECURITY_STATUS status,DWORD attributes)97 base::Value::Dict InitializeSecurityContextParams(SSPILibrary* library,
98                                                   PCtxtHandle handle,
99                                                   Error result,
100                                                   SECURITY_STATUS status,
101                                                   DWORD attributes) {
102   base::Value::Dict params;
103   params.Set("status", SecurityStatusToValue(result, status));
104   if (result == OK) {
105     params.Set("context",
106                ContextAttributesToValue(library, handle, attributes));
107   }
108   return params;
109 }
110 
MapAcquireCredentialsStatusToError(SECURITY_STATUS status)111 Error MapAcquireCredentialsStatusToError(SECURITY_STATUS status) {
112   switch (status) {
113     case SEC_E_OK:
114       return OK;
115     case SEC_E_INSUFFICIENT_MEMORY:
116       return ERR_OUT_OF_MEMORY;
117     case SEC_E_INTERNAL_ERROR:
118       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
119     case SEC_E_NO_CREDENTIALS:
120     case SEC_E_NOT_OWNER:
121     case SEC_E_UNKNOWN_CREDENTIALS:
122       return ERR_INVALID_AUTH_CREDENTIALS;
123     case SEC_E_SECPKG_NOT_FOUND:
124       // This indicates that the SSPI configuration does not match expectations
125       return ERR_UNSUPPORTED_AUTH_SCHEME;
126     default:
127       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
128   }
129 }
130 
AcquireExplicitCredentials(SSPILibrary * library,const std::u16string & domain,const std::u16string & user,const std::u16string & password,const NetLogWithSource & net_log,CredHandle * cred)131 Error AcquireExplicitCredentials(SSPILibrary* library,
132                                  const std::u16string& domain,
133                                  const std::u16string& user,
134                                  const std::u16string& password,
135                                  const NetLogWithSource& net_log,
136                                  CredHandle* cred) {
137   SEC_WINNT_AUTH_IDENTITY identity;
138   identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
139   identity.User = reinterpret_cast<unsigned short*>(
140       const_cast<wchar_t*>(base::as_wcstr(user)));
141   identity.UserLength = user.size();
142   identity.Domain = reinterpret_cast<unsigned short*>(
143       const_cast<wchar_t*>(base::as_wcstr(domain)));
144   identity.DomainLength = domain.size();
145   identity.Password = reinterpret_cast<unsigned short*>(
146       const_cast<wchar_t*>(base::as_wcstr(password)));
147   identity.PasswordLength = password.size();
148 
149   TimeStamp expiry;
150 
151   net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS);
152 
153   // Pass the username/password to get the credentials handle.
154   SECURITY_STATUS status = library->AcquireCredentialsHandle(
155       nullptr,                          // pszPrincipal
156       SECPKG_CRED_OUTBOUND,             // fCredentialUse
157       nullptr,                          // pvLogonID
158       &identity,                        // pAuthData
159       nullptr,                          // pGetKeyFn (not used)
160       nullptr,                          // pvGetKeyArgument (not used)
161       cred,                             // phCredential
162       &expiry);                         // ptsExpiry
163 
164   auto result = MapAcquireCredentialsStatusToError(status);
165   net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS, [&] {
166     return AcquireCredentialsHandleParams(&domain, &user, result, status);
167   });
168   return result;
169 }
170 
AcquireDefaultCredentials(SSPILibrary * library,const NetLogWithSource & net_log,CredHandle * cred)171 Error AcquireDefaultCredentials(SSPILibrary* library,
172                                 const NetLogWithSource& net_log,
173                                 CredHandle* cred) {
174   TimeStamp expiry;
175   net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS);
176 
177   // Pass the username/password to get the credentials handle.
178   // Note: Since the 5th argument is nullptr, it uses the default
179   // cached credentials for the logged in user, which can be used
180   // for a single sign-on.
181   SECURITY_STATUS status = library->AcquireCredentialsHandle(
182       nullptr,                          // pszPrincipal
183       SECPKG_CRED_OUTBOUND,             // fCredentialUse
184       nullptr,                          // pvLogonID
185       nullptr,                          // pAuthData
186       nullptr,                          // pGetKeyFn (not used)
187       nullptr,                          // pvGetKeyArgument (not used)
188       cred,                             // phCredential
189       &expiry);                         // ptsExpiry
190 
191   auto result = MapAcquireCredentialsStatusToError(status);
192   net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS, [&] {
193     return AcquireCredentialsHandleParams(nullptr, nullptr, result, status);
194   });
195   return result;
196 }
197 
MapInitializeSecurityContextStatusToError(SECURITY_STATUS status)198 Error MapInitializeSecurityContextStatusToError(SECURITY_STATUS status) {
199   switch (status) {
200     case SEC_E_OK:
201     case SEC_I_CONTINUE_NEEDED:
202       return OK;
203     case SEC_I_COMPLETE_AND_CONTINUE:
204     case SEC_I_COMPLETE_NEEDED:
205     case SEC_I_INCOMPLETE_CREDENTIALS:
206     case SEC_E_INCOMPLETE_MESSAGE:
207     case SEC_E_INTERNAL_ERROR:
208       // These are return codes reported by InitializeSecurityContext
209       // but not expected by Chrome (for example, INCOMPLETE_CREDENTIALS
210       // and INCOMPLETE_MESSAGE are intended for schannel).
211       return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
212     case SEC_E_INSUFFICIENT_MEMORY:
213       return ERR_OUT_OF_MEMORY;
214     case SEC_E_UNSUPPORTED_FUNCTION:
215       DUMP_WILL_BE_NOTREACHED_NORETURN();
216       return ERR_UNEXPECTED;
217     case SEC_E_INVALID_HANDLE:
218       DUMP_WILL_BE_NOTREACHED_NORETURN();
219       return ERR_INVALID_HANDLE;
220     case SEC_E_INVALID_TOKEN:
221       return ERR_INVALID_RESPONSE;
222     case SEC_E_LOGON_DENIED:
223     case SEC_E_NO_CREDENTIALS:
224     case SEC_E_WRONG_PRINCIPAL:
225       return ERR_INVALID_AUTH_CREDENTIALS;
226     case SEC_E_NO_AUTHENTICATING_AUTHORITY:
227     case SEC_E_TARGET_UNKNOWN:
228       return ERR_MISCONFIGURED_AUTH_ENVIRONMENT;
229     default:
230       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
231   }
232 }
233 
MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status)234 Error MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status) {
235   switch (status) {
236     case SEC_E_OK:
237       return OK;
238     case SEC_E_SECPKG_NOT_FOUND:
239       // This isn't a documented return code, but has been encountered
240       // during testing.
241       return ERR_UNSUPPORTED_AUTH_SCHEME;
242     default:
243       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
244   }
245 }
246 
MapFreeContextBufferStatusToError(SECURITY_STATUS status)247 Error MapFreeContextBufferStatusToError(SECURITY_STATUS status) {
248   switch (status) {
249     case SEC_E_OK:
250       return OK;
251     default:
252       // The documentation at
253       // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx
254       // only mentions that a non-zero (or non-SEC_E_OK) value is returned
255       // if the function fails, and does not indicate what the failure
256       // conditions are.
257       return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
258   }
259 }
260 
261 }  // anonymous namespace
262 
DetermineMaxTokenLength(ULONG * max_token_length)263 Error SSPILibrary::DetermineMaxTokenLength(ULONG* max_token_length) {
264   if (!is_supported_)
265     return ERR_UNSUPPORTED_AUTH_SCHEME;
266 
267   if (max_token_length_ != 0) {
268     *max_token_length = max_token_length_;
269     return OK;
270   }
271 
272   DCHECK(max_token_length);
273   PSecPkgInfo pkg_info = nullptr;
274   is_supported_ = false;
275 
276   SECURITY_STATUS status = QuerySecurityPackageInfo(&pkg_info);
277   Error rv = MapQuerySecurityPackageInfoStatusToError(status);
278   if (rv != OK)
279     return rv;
280   int token_length = pkg_info->cbMaxToken;
281 
282   status = FreeContextBuffer(pkg_info);
283   rv = MapFreeContextBufferStatusToError(status);
284   if (rv != OK)
285     return rv;
286   *max_token_length = max_token_length_ = token_length;
287   is_supported_ = true;
288   return OK;
289 }
290 
AcquireCredentialsHandle(LPWSTR pszPrincipal,unsigned long fCredentialUse,void * pvLogonId,void * pvAuthData,SEC_GET_KEY_FN pGetKeyFn,void * pvGetKeyArgument,PCredHandle phCredential,PTimeStamp ptsExpiry)291 SECURITY_STATUS SSPILibraryDefault::AcquireCredentialsHandle(
292     LPWSTR pszPrincipal,
293     unsigned long fCredentialUse,
294     void* pvLogonId,
295     void* pvAuthData,
296     SEC_GET_KEY_FN pGetKeyFn,
297     void* pvGetKeyArgument,
298     PCredHandle phCredential,
299     PTimeStamp ptsExpiry) {
300   return ::AcquireCredentialsHandleW(
301       pszPrincipal, const_cast<LPWSTR>(package_name_.c_str()), fCredentialUse,
302       pvLogonId, pvAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
303       ptsExpiry);
304 }
305 
InitializeSecurityContext(PCredHandle phCredential,PCtxtHandle phContext,SEC_WCHAR * pszTargetName,unsigned long fContextReq,unsigned long Reserved1,unsigned long TargetDataRep,PSecBufferDesc pInput,unsigned long Reserved2,PCtxtHandle phNewContext,PSecBufferDesc pOutput,unsigned long * contextAttr,PTimeStamp ptsExpiry)306 SECURITY_STATUS SSPILibraryDefault::InitializeSecurityContext(
307     PCredHandle phCredential,
308     PCtxtHandle phContext,
309     SEC_WCHAR* pszTargetName,
310     unsigned long fContextReq,
311     unsigned long Reserved1,
312     unsigned long TargetDataRep,
313     PSecBufferDesc pInput,
314     unsigned long Reserved2,
315     PCtxtHandle phNewContext,
316     PSecBufferDesc pOutput,
317     unsigned long* contextAttr,
318     PTimeStamp ptsExpiry) {
319   return ::InitializeSecurityContextW(phCredential, phContext, pszTargetName,
320                                       fContextReq, Reserved1, TargetDataRep,
321                                       pInput, Reserved2, phNewContext, pOutput,
322                                       contextAttr, ptsExpiry);
323 }
324 
QueryContextAttributesEx(PCtxtHandle phContext,ULONG ulAttribute,PVOID pBuffer,ULONG cbBuffer)325 SECURITY_STATUS SSPILibraryDefault::QueryContextAttributesEx(
326     PCtxtHandle phContext,
327     ULONG ulAttribute,
328     PVOID pBuffer,
329     ULONG cbBuffer) {
330   // TODO(https://crbug.com/992779): QueryContextAttributesExW is not included
331   // in Secur32.Lib in 10.0.18362.0 SDK. This symbol requires switching to using
332   // Windows SDK API sets in mincore.lib or OneCore.Lib. Switch to using
333   // QueryContextAttributesEx when the switch is made.
334   return ::QueryContextAttributes(phContext, ulAttribute, pBuffer);
335 }
336 
QuerySecurityPackageInfo(PSecPkgInfoW * pkgInfo)337 SECURITY_STATUS SSPILibraryDefault::QuerySecurityPackageInfo(
338     PSecPkgInfoW* pkgInfo) {
339   return ::QuerySecurityPackageInfoW(const_cast<LPWSTR>(package_name_.c_str()),
340                                      pkgInfo);
341 }
342 
FreeCredentialsHandle(PCredHandle phCredential)343 SECURITY_STATUS SSPILibraryDefault::FreeCredentialsHandle(
344     PCredHandle phCredential) {
345   return ::FreeCredentialsHandle(phCredential);
346 }
347 
DeleteSecurityContext(PCtxtHandle phContext)348 SECURITY_STATUS SSPILibraryDefault::DeleteSecurityContext(
349     PCtxtHandle phContext) {
350   return ::DeleteSecurityContext(phContext);
351 }
352 
FreeContextBuffer(PVOID pvContextBuffer)353 SECURITY_STATUS SSPILibraryDefault::FreeContextBuffer(PVOID pvContextBuffer) {
354   return ::FreeContextBuffer(pvContextBuffer);
355 }
356 
HttpAuthSSPI(SSPILibrary * library,HttpAuth::Scheme scheme)357 HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library, HttpAuth::Scheme scheme)
358     : library_(library),
359       scheme_(scheme),
360       delegation_type_(DelegationType::kNone) {
361   DCHECK(library_);
362   DCHECK(scheme_ == HttpAuth::AUTH_SCHEME_NEGOTIATE ||
363          scheme_ == HttpAuth::AUTH_SCHEME_NTLM);
364   SecInvalidateHandle(&cred_);
365   SecInvalidateHandle(&ctxt_);
366 }
367 
~HttpAuthSSPI()368 HttpAuthSSPI::~HttpAuthSSPI() {
369   ResetSecurityContext();
370   if (SecIsValidHandle(&cred_)) {
371     library_->FreeCredentialsHandle(&cred_);
372     SecInvalidateHandle(&cred_);
373   }
374 }
375 
Init(const NetLogWithSource &)376 bool HttpAuthSSPI::Init(const NetLogWithSource&) {
377   return true;
378 }
379 
NeedsIdentity() const380 bool HttpAuthSSPI::NeedsIdentity() const {
381   return decoded_server_auth_token_.empty();
382 }
383 
AllowsExplicitCredentials() const384 bool HttpAuthSSPI::AllowsExplicitCredentials() const {
385   return true;
386 }
387 
SetDelegation(DelegationType delegation_type)388 void HttpAuthSSPI::SetDelegation(DelegationType delegation_type) {
389   delegation_type_ = delegation_type;
390 }
391 
ResetSecurityContext()392 void HttpAuthSSPI::ResetSecurityContext() {
393   if (SecIsValidHandle(&ctxt_)) {
394     library_->DeleteSecurityContext(&ctxt_);
395     SecInvalidateHandle(&ctxt_);
396   }
397 }
398 
ParseChallenge(HttpAuthChallengeTokenizer * tok)399 HttpAuth::AuthorizationResult HttpAuthSSPI::ParseChallenge(
400     HttpAuthChallengeTokenizer* tok) {
401   if (!SecIsValidHandle(&ctxt_)) {
402     return net::ParseFirstRoundChallenge(scheme_, tok);
403   }
404   std::string encoded_auth_token;
405   return net::ParseLaterRoundChallenge(scheme_, tok, &encoded_auth_token,
406                                        &decoded_server_auth_token_);
407 }
408 
GenerateAuthToken(const AuthCredentials * credentials,const std::string & spn,const std::string & channel_bindings,std::string * auth_token,const NetLogWithSource & net_log,CompletionOnceCallback)409 int HttpAuthSSPI::GenerateAuthToken(const AuthCredentials* credentials,
410                                     const std::string& spn,
411                                     const std::string& channel_bindings,
412                                     std::string* auth_token,
413                                     const NetLogWithSource& net_log,
414                                     CompletionOnceCallback /*callback*/) {
415   // Initial challenge.
416   if (!SecIsValidHandle(&cred_)) {
417     // ParseChallenge fails early if a non-empty token is received on the first
418     // challenge.
419     DCHECK(decoded_server_auth_token_.empty());
420     int rv = OnFirstRound(credentials, net_log);
421     if (rv != OK)
422       return rv;
423   }
424 
425   DCHECK(SecIsValidHandle(&cred_));
426   void* out_buf;
427   int out_buf_len;
428   int rv = GetNextSecurityToken(
429       spn, channel_bindings,
430       static_cast<void*>(const_cast<char*>(decoded_server_auth_token_.c_str())),
431       decoded_server_auth_token_.length(), net_log, &out_buf, &out_buf_len);
432   if (rv != OK)
433     return rv;
434 
435   // Base64 encode data in output buffer and prepend the scheme.
436   std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
437   std::string encode_output = base::Base64Encode(encode_input);
438   // OK, we are done with |out_buf|
439   free(out_buf);
440   if (scheme_ == HttpAuth::AUTH_SCHEME_NEGOTIATE) {
441     *auth_token = "Negotiate " + encode_output;
442   } else {
443     *auth_token = "NTLM " + encode_output;
444   }
445   return OK;
446 }
447 
OnFirstRound(const AuthCredentials * credentials,const NetLogWithSource & net_log)448 int HttpAuthSSPI::OnFirstRound(const AuthCredentials* credentials,
449                                const NetLogWithSource& net_log) {
450   DCHECK(!SecIsValidHandle(&cred_));
451   int rv = OK;
452   if (credentials) {
453     std::u16string domain;
454     std::u16string user;
455     SplitDomainAndUser(credentials->username(), &domain, &user);
456     rv = AcquireExplicitCredentials(library_, domain, user,
457                                     credentials->password(), net_log, &cred_);
458     if (rv != OK)
459       return rv;
460   } else {
461     rv = AcquireDefaultCredentials(library_, net_log, &cred_);
462     if (rv != OK)
463       return rv;
464   }
465 
466   return rv;
467 }
468 
GetNextSecurityToken(const std::string & spn,const std::string & channel_bindings,const void * in_token,int in_token_len,const NetLogWithSource & net_log,void ** out_token,int * out_token_len)469 int HttpAuthSSPI::GetNextSecurityToken(const std::string& spn,
470                                        const std::string& channel_bindings,
471                                        const void* in_token,
472                                        int in_token_len,
473                                        const NetLogWithSource& net_log,
474                                        void** out_token,
475                                        int* out_token_len) {
476   ULONG max_token_length = 0;
477   // Microsoft SDKs have a loose relationship with const.
478   Error rv = library_->DetermineMaxTokenLength(&max_token_length);
479   if (rv != OK)
480     return rv;
481 
482   CtxtHandle* ctxt_ptr = nullptr;
483   SecBufferDesc in_buffer_desc, out_buffer_desc;
484   SecBufferDesc* in_buffer_desc_ptr = nullptr;
485   SecBuffer in_buffers[2], out_buffer;
486 
487   in_buffer_desc.ulVersion = SECBUFFER_VERSION;
488   in_buffer_desc.cBuffers = 0;
489   in_buffer_desc.pBuffers = in_buffers;
490   if (in_token_len > 0) {
491     // Prepare input buffer.
492     SecBuffer& sec_buffer = in_buffers[in_buffer_desc.cBuffers++];
493     sec_buffer.BufferType = SECBUFFER_TOKEN;
494     sec_buffer.cbBuffer = in_token_len;
495     sec_buffer.pvBuffer = const_cast<void*>(in_token);
496     ctxt_ptr = &ctxt_;
497   } else {
498     // If there is no input token, then we are starting a new authentication
499     // sequence.  If we have already initialized our security context, then
500     // we're incorrectly reusing the auth handler for a new sequence.
501     if (SecIsValidHandle(&ctxt_)) {
502       return ERR_UNEXPECTED;
503     }
504   }
505 
506   std::vector<char> sec_channel_bindings_buffer;
507   if (!channel_bindings.empty()) {
508     sec_channel_bindings_buffer.reserve(sizeof(SEC_CHANNEL_BINDINGS) +
509                                         channel_bindings.size());
510     sec_channel_bindings_buffer.resize(sizeof(SEC_CHANNEL_BINDINGS));
511     SEC_CHANNEL_BINDINGS* bindings_desc =
512         reinterpret_cast<SEC_CHANNEL_BINDINGS*>(
513             sec_channel_bindings_buffer.data());
514     bindings_desc->cbApplicationDataLength = channel_bindings.size();
515     bindings_desc->dwApplicationDataOffset = sizeof(SEC_CHANNEL_BINDINGS);
516     sec_channel_bindings_buffer.insert(sec_channel_bindings_buffer.end(),
517                                        channel_bindings.begin(),
518                                        channel_bindings.end());
519     DCHECK_EQ(sizeof(SEC_CHANNEL_BINDINGS) + channel_bindings.size(),
520               sec_channel_bindings_buffer.size());
521 
522     SecBuffer& sec_buffer = in_buffers[in_buffer_desc.cBuffers++];
523     sec_buffer.BufferType = SECBUFFER_CHANNEL_BINDINGS;
524     sec_buffer.cbBuffer = sec_channel_bindings_buffer.size();
525     sec_buffer.pvBuffer = sec_channel_bindings_buffer.data();
526   }
527 
528   if (in_buffer_desc.cBuffers > 0)
529     in_buffer_desc_ptr = &in_buffer_desc;
530 
531   // Prepare output buffer.
532   out_buffer_desc.ulVersion = SECBUFFER_VERSION;
533   out_buffer_desc.cBuffers = 1;
534   out_buffer_desc.pBuffers = &out_buffer;
535   out_buffer.BufferType = SECBUFFER_TOKEN;
536   out_buffer.cbBuffer = max_token_length;
537   out_buffer.pvBuffer = malloc(out_buffer.cbBuffer);
538   if (!out_buffer.pvBuffer)
539     return ERR_OUT_OF_MEMORY;
540 
541   DWORD context_flags = 0;
542   // Firefox only sets ISC_REQ_DELEGATE, but MSDN documentation indicates that
543   // ISC_REQ_MUTUAL_AUTH must also be set. On Windows delegation by KDC policy
544   // is always respected.
545   if (delegation_type_ != DelegationType::kNone)
546     context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH);
547 
548   net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] {
549     base::Value::Dict params;
550     params.Set("spn", spn);
551     params.Set("flags", ContextFlagsToValue(context_flags));
552     return params;
553   });
554 
555   // This returns a token that is passed to the remote server.
556   DWORD context_attributes = 0;
557   std::u16string spn16 = base::ASCIIToUTF16(spn);
558   SECURITY_STATUS status = library_->InitializeSecurityContext(
559       &cred_,                          // phCredential
560       ctxt_ptr,                        // phContext
561       base::as_writable_wcstr(spn16),  // pszTargetName
562       context_flags,                   // fContextReq
563       0,                               // Reserved1 (must be 0)
564       SECURITY_NATIVE_DREP,            // TargetDataRep
565       in_buffer_desc_ptr,              // pInput
566       0,                               // Reserved2 (must be 0)
567       &ctxt_,                          // phNewContext
568       &out_buffer_desc,                // pOutput
569       &context_attributes,             // pfContextAttr
570       nullptr);                        // ptsExpiry
571   rv = MapInitializeSecurityContextStatusToError(status);
572   net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] {
573     return InitializeSecurityContextParams(library_, &ctxt_, rv, status,
574                                            context_attributes);
575   });
576 
577   if (rv != OK) {
578     ResetSecurityContext();
579     free(out_buffer.pvBuffer);
580     return rv;
581   }
582   if (!out_buffer.cbBuffer) {
583     free(out_buffer.pvBuffer);
584     out_buffer.pvBuffer = nullptr;
585   }
586   *out_token = out_buffer.pvBuffer;
587   *out_token_len = out_buffer.cbBuffer;
588   return OK;
589 }
590 
SplitDomainAndUser(const std::u16string & combined,std::u16string * domain,std::u16string * user)591 void SplitDomainAndUser(const std::u16string& combined,
592                         std::u16string* domain,
593                         std::u16string* user) {
594   // |combined| may be in the form "user" or "DOMAIN\user".
595   // Separate the two parts if they exist.
596   // TODO(cbentzel): I believe user@domain is also a valid form.
597   size_t backslash_idx = combined.find(L'\\');
598   if (backslash_idx == std::u16string::npos) {
599     domain->clear();
600     *user = combined;
601   } else {
602     *domain = combined.substr(0, backslash_idx);
603     *user = combined.substr(backslash_idx + 1);
604   }
605 }
606 
607 }  // namespace net
608