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