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