| // Copyright 2011 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // See "SSPI Sample Application" at |
| // http://msdn.microsoft.com/en-us/library/aa918273.aspx |
| |
| #include "net/http/http_auth_sspi_win.h" |
| |
| #include "base/base64.h" |
| #include "base/logging.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/values.h" |
| #include "net/base/net_errors.h" |
| #include "net/http/http_auth.h" |
| #include "net/http/http_auth_multi_round_parse.h" |
| #include "net/log/net_log.h" |
| #include "net/log/net_log_event_type.h" |
| #include "net/log/net_log_values.h" |
| #include "net/log/net_log_with_source.h" |
| |
| namespace net { |
| using DelegationType = HttpAuth::DelegationType; |
| |
| namespace { |
| |
| base::Value::Dict SecurityStatusToValue(Error mapped_error, |
| SECURITY_STATUS status) { |
| base::Value::Dict params; |
| params.Set("net_error", mapped_error); |
| params.Set("security_status", static_cast<int>(status)); |
| return params; |
| } |
| |
| base::Value::Dict AcquireCredentialsHandleParams(const std::u16string* domain, |
| const std::u16string* user, |
| Error result, |
| SECURITY_STATUS status) { |
| base::Value::Dict params; |
| if (domain && user) { |
| params.Set("domain", base::UTF16ToUTF8(*domain)); |
| params.Set("user", base::UTF16ToUTF8(*user)); |
| } |
| params.Set("status", SecurityStatusToValue(result, status)); |
| return params; |
| } |
| |
| base::Value::Dict ContextFlagsToValue(DWORD flags) { |
| base::Value::Dict params; |
| params.Set("value", base::StringPrintf("0x%08lx", flags)); |
| params.Set("delegated", (flags & ISC_RET_DELEGATE) == ISC_RET_DELEGATE); |
| params.Set("mutual", (flags & ISC_RET_MUTUAL_AUTH) == ISC_RET_MUTUAL_AUTH); |
| return params; |
| } |
| |
| base::Value::Dict ContextAttributesToValue(SSPILibrary* library, |
| PCtxtHandle handle, |
| DWORD attributes) { |
| base::Value::Dict params; |
| |
| SecPkgContext_NativeNames native_names = {0}; |
| auto qc_result = library->QueryContextAttributesEx( |
| handle, SECPKG_ATTR_NATIVE_NAMES, &native_names, sizeof(native_names)); |
| if (qc_result == SEC_E_OK && native_names.sClientName && |
| native_names.sServerName) { |
| params.Set("source", base::as_u16cstr(native_names.sClientName)); |
| params.Set("target", base::as_u16cstr(native_names.sServerName)); |
| } |
| |
| SecPkgContext_NegotiationInfo negotiation_info = {0}; |
| qc_result = library->QueryContextAttributesEx( |
| handle, SECPKG_ATTR_NEGOTIATION_INFO, &negotiation_info, |
| sizeof(negotiation_info)); |
| if (qc_result == SEC_E_OK && negotiation_info.PackageInfo && |
| negotiation_info.PackageInfo->Name) { |
| params.Set("mechanism", |
| base::as_u16cstr(negotiation_info.PackageInfo->Name)); |
| params.Set("open", negotiation_info.NegotiationState != |
| SECPKG_NEGOTIATION_COMPLETE); |
| } |
| |
| SecPkgContext_Authority authority = {0}; |
| qc_result = library->QueryContextAttributesEx(handle, SECPKG_ATTR_AUTHORITY, |
| &authority, sizeof(authority)); |
| if (qc_result == SEC_E_OK && authority.sAuthorityName) { |
| params.Set("authority", base::as_u16cstr(authority.sAuthorityName)); |
| } |
| |
| params.Set("flags", ContextFlagsToValue(attributes)); |
| return params; |
| } |
| |
| base::Value::Dict InitializeSecurityContextParams(SSPILibrary* library, |
| PCtxtHandle handle, |
| Error result, |
| SECURITY_STATUS status, |
| DWORD attributes) { |
| base::Value::Dict params; |
| params.Set("status", SecurityStatusToValue(result, status)); |
| if (result == OK) { |
| params.Set("context", |
| ContextAttributesToValue(library, handle, attributes)); |
| } |
| return params; |
| } |
| |
| Error MapAcquireCredentialsStatusToError(SECURITY_STATUS status) { |
| switch (status) { |
| case SEC_E_OK: |
| return OK; |
| case SEC_E_INSUFFICIENT_MEMORY: |
| return ERR_OUT_OF_MEMORY; |
| case SEC_E_INTERNAL_ERROR: |
| return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
| case SEC_E_NO_CREDENTIALS: |
| case SEC_E_NOT_OWNER: |
| case SEC_E_UNKNOWN_CREDENTIALS: |
| return ERR_INVALID_AUTH_CREDENTIALS; |
| case SEC_E_SECPKG_NOT_FOUND: |
| // This indicates that the SSPI configuration does not match expectations |
| return ERR_UNSUPPORTED_AUTH_SCHEME; |
| default: |
| return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
| } |
| } |
| |
| Error AcquireExplicitCredentials(SSPILibrary* library, |
| const std::u16string& domain, |
| const std::u16string& user, |
| const std::u16string& password, |
| const NetLogWithSource& net_log, |
| CredHandle* cred) { |
| SEC_WINNT_AUTH_IDENTITY identity; |
| identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE; |
| identity.User = reinterpret_cast<unsigned short*>( |
| const_cast<wchar_t*>(base::as_wcstr(user))); |
| identity.UserLength = user.size(); |
| identity.Domain = reinterpret_cast<unsigned short*>( |
| const_cast<wchar_t*>(base::as_wcstr(domain))); |
| identity.DomainLength = domain.size(); |
| identity.Password = reinterpret_cast<unsigned short*>( |
| const_cast<wchar_t*>(base::as_wcstr(password))); |
| identity.PasswordLength = password.size(); |
| |
| TimeStamp expiry; |
| |
| net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS); |
| |
| // Pass the username/password to get the credentials handle. |
| SECURITY_STATUS status = library->AcquireCredentialsHandle( |
| nullptr, // pszPrincipal |
| SECPKG_CRED_OUTBOUND, // fCredentialUse |
| nullptr, // pvLogonID |
| &identity, // pAuthData |
| nullptr, // pGetKeyFn (not used) |
| nullptr, // pvGetKeyArgument (not used) |
| cred, // phCredential |
| &expiry); // ptsExpiry |
| |
| auto result = MapAcquireCredentialsStatusToError(status); |
| net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS, [&] { |
| return AcquireCredentialsHandleParams(&domain, &user, result, status); |
| }); |
| return result; |
| } |
| |
| Error AcquireDefaultCredentials(SSPILibrary* library, |
| const NetLogWithSource& net_log, |
| CredHandle* cred) { |
| TimeStamp expiry; |
| net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS); |
| |
| // Pass the username/password to get the credentials handle. |
| // Note: Since the 5th argument is nullptr, it uses the default |
| // cached credentials for the logged in user, which can be used |
| // for a single sign-on. |
| SECURITY_STATUS status = library->AcquireCredentialsHandle( |
| nullptr, // pszPrincipal |
| SECPKG_CRED_OUTBOUND, // fCredentialUse |
| nullptr, // pvLogonID |
| nullptr, // pAuthData |
| nullptr, // pGetKeyFn (not used) |
| nullptr, // pvGetKeyArgument (not used) |
| cred, // phCredential |
| &expiry); // ptsExpiry |
| |
| auto result = MapAcquireCredentialsStatusToError(status); |
| net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_ACQUIRE_CREDS, [&] { |
| return AcquireCredentialsHandleParams(nullptr, nullptr, result, status); |
| }); |
| return result; |
| } |
| |
| Error MapInitializeSecurityContextStatusToError(SECURITY_STATUS status) { |
| switch (status) { |
| case SEC_E_OK: |
| case SEC_I_CONTINUE_NEEDED: |
| return OK; |
| case SEC_I_COMPLETE_AND_CONTINUE: |
| case SEC_I_COMPLETE_NEEDED: |
| case SEC_I_INCOMPLETE_CREDENTIALS: |
| case SEC_E_INCOMPLETE_MESSAGE: |
| case SEC_E_INTERNAL_ERROR: |
| // These are return codes reported by InitializeSecurityContext |
| // but not expected by Chrome (for example, INCOMPLETE_CREDENTIALS |
| // and INCOMPLETE_MESSAGE are intended for schannel). |
| return ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS; |
| case SEC_E_INSUFFICIENT_MEMORY: |
| return ERR_OUT_OF_MEMORY; |
| case SEC_E_UNSUPPORTED_FUNCTION: |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| case SEC_E_INVALID_HANDLE: |
| NOTREACHED(); |
| return ERR_INVALID_HANDLE; |
| case SEC_E_INVALID_TOKEN: |
| return ERR_INVALID_RESPONSE; |
| case SEC_E_LOGON_DENIED: |
| case SEC_E_NO_CREDENTIALS: |
| case SEC_E_WRONG_PRINCIPAL: |
| return ERR_INVALID_AUTH_CREDENTIALS; |
| case SEC_E_NO_AUTHENTICATING_AUTHORITY: |
| case SEC_E_TARGET_UNKNOWN: |
| return ERR_MISCONFIGURED_AUTH_ENVIRONMENT; |
| default: |
| return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
| } |
| } |
| |
| Error MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status) { |
| switch (status) { |
| case SEC_E_OK: |
| return OK; |
| case SEC_E_SECPKG_NOT_FOUND: |
| // This isn't a documented return code, but has been encountered |
| // during testing. |
| return ERR_UNSUPPORTED_AUTH_SCHEME; |
| default: |
| return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
| } |
| } |
| |
| Error MapFreeContextBufferStatusToError(SECURITY_STATUS status) { |
| switch (status) { |
| case SEC_E_OK: |
| return OK; |
| default: |
| // The documentation at |
| // http://msdn.microsoft.com/en-us/library/aa375416(VS.85).aspx |
| // only mentions that a non-zero (or non-SEC_E_OK) value is returned |
| // if the function fails, and does not indicate what the failure |
| // conditions are. |
| return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS; |
| } |
| } |
| |
| } // anonymous namespace |
| |
| Error SSPILibrary::DetermineMaxTokenLength(ULONG* max_token_length) { |
| if (!is_supported_) |
| return ERR_UNSUPPORTED_AUTH_SCHEME; |
| |
| if (max_token_length_ != 0) { |
| *max_token_length = max_token_length_; |
| return OK; |
| } |
| |
| DCHECK(max_token_length); |
| PSecPkgInfo pkg_info = nullptr; |
| is_supported_ = false; |
| |
| SECURITY_STATUS status = QuerySecurityPackageInfo(&pkg_info); |
| Error rv = MapQuerySecurityPackageInfoStatusToError(status); |
| if (rv != OK) |
| return rv; |
| int token_length = pkg_info->cbMaxToken; |
| |
| status = FreeContextBuffer(pkg_info); |
| rv = MapFreeContextBufferStatusToError(status); |
| if (rv != OK) |
| return rv; |
| *max_token_length = max_token_length_ = token_length; |
| is_supported_ = true; |
| return OK; |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::AcquireCredentialsHandle( |
| LPWSTR pszPrincipal, |
| unsigned long fCredentialUse, |
| void* pvLogonId, |
| void* pvAuthData, |
| SEC_GET_KEY_FN pGetKeyFn, |
| void* pvGetKeyArgument, |
| PCredHandle phCredential, |
| PTimeStamp ptsExpiry) { |
| return ::AcquireCredentialsHandleW( |
| pszPrincipal, const_cast<LPWSTR>(package_name_.c_str()), fCredentialUse, |
| pvLogonId, pvAuthData, pGetKeyFn, pvGetKeyArgument, phCredential, |
| ptsExpiry); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::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) { |
| return ::InitializeSecurityContextW(phCredential, phContext, pszTargetName, |
| fContextReq, Reserved1, TargetDataRep, |
| pInput, Reserved2, phNewContext, pOutput, |
| contextAttr, ptsExpiry); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::QueryContextAttributesEx( |
| PCtxtHandle phContext, |
| ULONG ulAttribute, |
| PVOID pBuffer, |
| ULONG cbBuffer) { |
| // TODO(https://crbug.com/992779): QueryContextAttributesExW is not included |
| // in Secur32.Lib in 10.0.18362.0 SDK. This symbol requires switching to using |
| // Windows SDK API sets in mincore.lib or OneCore.Lib. Switch to using |
| // QueryContextAttributesEx when the switch is made. |
| return ::QueryContextAttributes(phContext, ulAttribute, pBuffer); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::QuerySecurityPackageInfo( |
| PSecPkgInfoW* pkgInfo) { |
| return ::QuerySecurityPackageInfoW(const_cast<LPWSTR>(package_name_.c_str()), |
| pkgInfo); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::FreeCredentialsHandle( |
| PCredHandle phCredential) { |
| return ::FreeCredentialsHandle(phCredential); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::DeleteSecurityContext( |
| PCtxtHandle phContext) { |
| return ::DeleteSecurityContext(phContext); |
| } |
| |
| SECURITY_STATUS SSPILibraryDefault::FreeContextBuffer(PVOID pvContextBuffer) { |
| return ::FreeContextBuffer(pvContextBuffer); |
| } |
| |
| HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library, HttpAuth::Scheme scheme) |
| : library_(library), |
| scheme_(scheme), |
| delegation_type_(DelegationType::kNone) { |
| DCHECK(library_); |
| DCHECK(scheme_ == HttpAuth::AUTH_SCHEME_NEGOTIATE || |
| scheme_ == HttpAuth::AUTH_SCHEME_NTLM); |
| SecInvalidateHandle(&cred_); |
| SecInvalidateHandle(&ctxt_); |
| } |
| |
| HttpAuthSSPI::~HttpAuthSSPI() { |
| ResetSecurityContext(); |
| if (SecIsValidHandle(&cred_)) { |
| library_->FreeCredentialsHandle(&cred_); |
| SecInvalidateHandle(&cred_); |
| } |
| } |
| |
| bool HttpAuthSSPI::Init(const NetLogWithSource&) { |
| return true; |
| } |
| |
| bool HttpAuthSSPI::NeedsIdentity() const { |
| return decoded_server_auth_token_.empty(); |
| } |
| |
| bool HttpAuthSSPI::AllowsExplicitCredentials() const { |
| return true; |
| } |
| |
| void HttpAuthSSPI::SetDelegation(DelegationType delegation_type) { |
| delegation_type_ = delegation_type; |
| } |
| |
| void HttpAuthSSPI::ResetSecurityContext() { |
| if (SecIsValidHandle(&ctxt_)) { |
| library_->DeleteSecurityContext(&ctxt_); |
| SecInvalidateHandle(&ctxt_); |
| } |
| } |
| |
| HttpAuth::AuthorizationResult HttpAuthSSPI::ParseChallenge( |
| HttpAuthChallengeTokenizer* tok) { |
| if (!SecIsValidHandle(&ctxt_)) { |
| return net::ParseFirstRoundChallenge(scheme_, tok); |
| } |
| std::string encoded_auth_token; |
| return net::ParseLaterRoundChallenge(scheme_, tok, &encoded_auth_token, |
| &decoded_server_auth_token_); |
| } |
| |
| int HttpAuthSSPI::GenerateAuthToken(const AuthCredentials* credentials, |
| const std::string& spn, |
| const std::string& channel_bindings, |
| std::string* auth_token, |
| const NetLogWithSource& net_log, |
| CompletionOnceCallback /*callback*/) { |
| // Initial challenge. |
| if (!SecIsValidHandle(&cred_)) { |
| // ParseChallenge fails early if a non-empty token is received on the first |
| // challenge. |
| DCHECK(decoded_server_auth_token_.empty()); |
| int rv = OnFirstRound(credentials, net_log); |
| if (rv != OK) |
| return rv; |
| } |
| |
| DCHECK(SecIsValidHandle(&cred_)); |
| void* out_buf; |
| int out_buf_len; |
| int rv = GetNextSecurityToken( |
| spn, channel_bindings, |
| static_cast<void*>(const_cast<char*>(decoded_server_auth_token_.c_str())), |
| decoded_server_auth_token_.length(), net_log, &out_buf, &out_buf_len); |
| if (rv != OK) |
| return rv; |
| |
| // Base64 encode data in output buffer and prepend the scheme. |
| std::string encode_input(static_cast<char*>(out_buf), out_buf_len); |
| std::string encode_output; |
| base::Base64Encode(encode_input, &encode_output); |
| // OK, we are done with |out_buf| |
| free(out_buf); |
| if (scheme_ == HttpAuth::AUTH_SCHEME_NEGOTIATE) { |
| *auth_token = "Negotiate " + encode_output; |
| } else { |
| *auth_token = "NTLM " + encode_output; |
| } |
| return OK; |
| } |
| |
| int HttpAuthSSPI::OnFirstRound(const AuthCredentials* credentials, |
| const NetLogWithSource& net_log) { |
| DCHECK(!SecIsValidHandle(&cred_)); |
| int rv = OK; |
| if (credentials) { |
| std::u16string domain; |
| std::u16string user; |
| SplitDomainAndUser(credentials->username(), &domain, &user); |
| rv = AcquireExplicitCredentials(library_, domain, user, |
| credentials->password(), net_log, &cred_); |
| if (rv != OK) |
| return rv; |
| } else { |
| rv = AcquireDefaultCredentials(library_, net_log, &cred_); |
| if (rv != OK) |
| return rv; |
| } |
| |
| return rv; |
| } |
| |
| int HttpAuthSSPI::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) { |
| ULONG max_token_length = 0; |
| // Microsoft SDKs have a loose relationship with const. |
| Error rv = library_->DetermineMaxTokenLength(&max_token_length); |
| if (rv != OK) |
| return rv; |
| |
| CtxtHandle* ctxt_ptr = nullptr; |
| SecBufferDesc in_buffer_desc, out_buffer_desc; |
| SecBufferDesc* in_buffer_desc_ptr = nullptr; |
| SecBuffer in_buffers[2], out_buffer; |
| |
| in_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| in_buffer_desc.cBuffers = 0; |
| in_buffer_desc.pBuffers = in_buffers; |
| if (in_token_len > 0) { |
| // Prepare input buffer. |
| SecBuffer& sec_buffer = in_buffers[in_buffer_desc.cBuffers++]; |
| sec_buffer.BufferType = SECBUFFER_TOKEN; |
| sec_buffer.cbBuffer = in_token_len; |
| sec_buffer.pvBuffer = const_cast<void*>(in_token); |
| ctxt_ptr = &ctxt_; |
| } else { |
| // If there is no input token, then we are starting a new authentication |
| // sequence. If we have already initialized our security context, then |
| // we're incorrectly reusing the auth handler for a new sequence. |
| if (SecIsValidHandle(&ctxt_)) { |
| NOTREACHED(); |
| return ERR_UNEXPECTED; |
| } |
| } |
| |
| std::vector<char> sec_channel_bindings_buffer; |
| if (!channel_bindings.empty()) { |
| sec_channel_bindings_buffer.reserve(sizeof(SEC_CHANNEL_BINDINGS) + |
| channel_bindings.size()); |
| sec_channel_bindings_buffer.resize(sizeof(SEC_CHANNEL_BINDINGS)); |
| SEC_CHANNEL_BINDINGS* bindings_desc = |
| reinterpret_cast<SEC_CHANNEL_BINDINGS*>( |
| sec_channel_bindings_buffer.data()); |
| bindings_desc->cbApplicationDataLength = channel_bindings.size(); |
| bindings_desc->dwApplicationDataOffset = sizeof(SEC_CHANNEL_BINDINGS); |
| sec_channel_bindings_buffer.insert(sec_channel_bindings_buffer.end(), |
| channel_bindings.begin(), |
| channel_bindings.end()); |
| DCHECK_EQ(sizeof(SEC_CHANNEL_BINDINGS) + channel_bindings.size(), |
| sec_channel_bindings_buffer.size()); |
| |
| SecBuffer& sec_buffer = in_buffers[in_buffer_desc.cBuffers++]; |
| sec_buffer.BufferType = SECBUFFER_CHANNEL_BINDINGS; |
| sec_buffer.cbBuffer = sec_channel_bindings_buffer.size(); |
| sec_buffer.pvBuffer = sec_channel_bindings_buffer.data(); |
| } |
| |
| if (in_buffer_desc.cBuffers > 0) |
| in_buffer_desc_ptr = &in_buffer_desc; |
| |
| // Prepare output buffer. |
| out_buffer_desc.ulVersion = SECBUFFER_VERSION; |
| out_buffer_desc.cBuffers = 1; |
| out_buffer_desc.pBuffers = &out_buffer; |
| out_buffer.BufferType = SECBUFFER_TOKEN; |
| out_buffer.cbBuffer = max_token_length; |
| out_buffer.pvBuffer = malloc(out_buffer.cbBuffer); |
| if (!out_buffer.pvBuffer) |
| return ERR_OUT_OF_MEMORY; |
| |
| DWORD context_flags = 0; |
| // Firefox only sets ISC_REQ_DELEGATE, but MSDN documentation indicates that |
| // ISC_REQ_MUTUAL_AUTH must also be set. On Windows delegation by KDC policy |
| // is always respected. |
| if (delegation_type_ != DelegationType::kNone) |
| context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH); |
| |
| net_log.BeginEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] { |
| base::Value::Dict params; |
| params.Set("spn", spn); |
| params.Set("flags", ContextFlagsToValue(context_flags)); |
| return params; |
| }); |
| |
| // This returns a token that is passed to the remote server. |
| DWORD context_attributes = 0; |
| std::u16string spn16 = base::ASCIIToUTF16(spn); |
| SECURITY_STATUS status = library_->InitializeSecurityContext( |
| &cred_, // phCredential |
| ctxt_ptr, // phContext |
| base::as_writable_wcstr(spn16), // pszTargetName |
| context_flags, // fContextReq |
| 0, // Reserved1 (must be 0) |
| SECURITY_NATIVE_DREP, // TargetDataRep |
| in_buffer_desc_ptr, // pInput |
| 0, // Reserved2 (must be 0) |
| &ctxt_, // phNewContext |
| &out_buffer_desc, // pOutput |
| &context_attributes, // pfContextAttr |
| nullptr); // ptsExpiry |
| rv = MapInitializeSecurityContextStatusToError(status); |
| net_log.EndEvent(NetLogEventType::AUTH_LIBRARY_INIT_SEC_CTX, [&] { |
| return InitializeSecurityContextParams(library_, &ctxt_, rv, status, |
| context_attributes); |
| }); |
| |
| if (rv != OK) { |
| ResetSecurityContext(); |
| free(out_buffer.pvBuffer); |
| return rv; |
| } |
| if (!out_buffer.cbBuffer) { |
| free(out_buffer.pvBuffer); |
| out_buffer.pvBuffer = nullptr; |
| } |
| *out_token = out_buffer.pvBuffer; |
| *out_token_len = out_buffer.cbBuffer; |
| return OK; |
| } |
| |
| void SplitDomainAndUser(const std::u16string& combined, |
| std::u16string* domain, |
| std::u16string* user) { |
| // |combined| may be in the form "user" or "DOMAIN\user". |
| // Separate the two parts if they exist. |
| // TODO(cbentzel): I believe user@domain is also a valid form. |
| size_t backslash_idx = combined.find(L'\\'); |
| if (backslash_idx == std::u16string::npos) { |
| domain->clear(); |
| *user = combined; |
| } else { |
| *domain = combined.substr(0, backslash_idx); |
| *user = combined.substr(backslash_idx + 1); |
| } |
| } |
| |
| } // namespace net |