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