This source file includes following definitions.
- MapAcquireCredentialsStatusToError
- AcquireExplicitCredentials
- AcquireDefaultCredentials
- MapInitializeSecurityContextStatusToError
- MapQuerySecurityPackageInfoStatusToError
- MapFreeContextBufferStatusToError
- can_delegate_
- NeedsIdentity
- AllowsExplicitCredentials
- Delegate
- ResetSecurityContext
- ParseChallenge
- GenerateAuthToken
- OnFirstRound
- GetNextSecurityToken
- SplitDomainAndUser
- DetermineMaxTokenLength
#include "net/http/http_auth_sspi_win.h"
#include "base/base64.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "net/base/net_errors.h"
#include "net/http/http_auth.h"
#include "net/http/http_auth_challenge_tokenizer.h"
namespace net {
namespace {
int MapAcquireCredentialsStatusToError(SECURITY_STATUS status,
const SEC_WCHAR* package) {
VLOG(1) << "AcquireCredentialsHandle returned 0x" << std::hex << status;
switch (status) {
case SEC_E_OK:
return OK;
case SEC_E_INSUFFICIENT_MEMORY:
return ERR_OUT_OF_MEMORY;
case SEC_E_INTERNAL_ERROR:
LOG(WARNING)
<< "AcquireCredentialsHandle returned unexpected status 0x"
<< std::hex << status;
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:
return ERR_UNSUPPORTED_AUTH_SCHEME;
default:
LOG(WARNING)
<< "AcquireCredentialsHandle returned undocumented status 0x"
<< std::hex << status;
return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
}
}
int AcquireExplicitCredentials(SSPILibrary* library,
const SEC_WCHAR* package,
const base::string16& domain,
const base::string16& user,
const base::string16& password,
CredHandle* cred) {
SEC_WINNT_AUTH_IDENTITY identity;
identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
identity.User =
reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(user.c_str()));
identity.UserLength = user.size();
identity.Domain =
reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(domain.c_str()));
identity.DomainLength = domain.size();
identity.Password =
reinterpret_cast<unsigned short*>(const_cast<wchar_t*>(password.c_str()));
identity.PasswordLength = password.size();
TimeStamp expiry;
SECURITY_STATUS status = library->AcquireCredentialsHandle(
NULL,
const_cast<SEC_WCHAR*>(package),
SECPKG_CRED_OUTBOUND,
NULL,
&identity,
NULL,
NULL,
cred,
&expiry);
return MapAcquireCredentialsStatusToError(status, package);
}
int AcquireDefaultCredentials(SSPILibrary* library, const SEC_WCHAR* package,
CredHandle* cred) {
TimeStamp expiry;
SECURITY_STATUS status = library->AcquireCredentialsHandle(
NULL,
const_cast<SEC_WCHAR*>(package),
SECPKG_CRED_OUTBOUND,
NULL,
NULL,
NULL,
NULL,
cred,
&expiry);
return MapAcquireCredentialsStatusToError(status, package);
}
int MapInitializeSecurityContextStatusToError(SECURITY_STATUS status) {
VLOG(1) << "InitializeSecurityContext returned 0x" << std::hex << 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:
LOG(WARNING)
<< "InitializeSecurityContext returned unexpected status 0x"
<< std::hex << status;
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:
return ERR_ACCESS_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:
LOG(WARNING)
<< "InitializeSecurityContext returned undocumented status 0x"
<< std::hex << status;
return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
}
}
int MapQuerySecurityPackageInfoStatusToError(SECURITY_STATUS status) {
VLOG(1) << "QuerySecurityPackageInfo returned 0x" << std::hex << status;
switch (status) {
case SEC_E_OK:
return OK;
case SEC_E_SECPKG_NOT_FOUND:
return ERR_UNSUPPORTED_AUTH_SCHEME;
default:
LOG(WARNING)
<< "QuerySecurityPackageInfo returned undocumented status 0x"
<< std::hex << status;
return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
}
}
int MapFreeContextBufferStatusToError(SECURITY_STATUS status) {
VLOG(1) << "FreeContextBuffer returned 0x" << std::hex << status;
switch (status) {
case SEC_E_OK:
return OK;
default:
LOG(WARNING)
<< "FreeContextBuffer returned undocumented status 0x"
<< std::hex << status;
return ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
}
}
}
HttpAuthSSPI::HttpAuthSSPI(SSPILibrary* library,
const std::string& scheme,
const SEC_WCHAR* security_package,
ULONG max_token_length)
: library_(library),
scheme_(scheme),
security_package_(security_package),
max_token_length_(max_token_length),
can_delegate_(false) {
DCHECK(library_);
SecInvalidateHandle(&cred_);
SecInvalidateHandle(&ctxt_);
}
HttpAuthSSPI::~HttpAuthSSPI() {
ResetSecurityContext();
if (SecIsValidHandle(&cred_)) {
library_->FreeCredentialsHandle(&cred_);
SecInvalidateHandle(&cred_);
}
}
bool HttpAuthSSPI::NeedsIdentity() const {
return decoded_server_auth_token_.empty();
}
bool HttpAuthSSPI::AllowsExplicitCredentials() const {
return true;
}
void HttpAuthSSPI::Delegate() {
can_delegate_ = true;
}
void HttpAuthSSPI::ResetSecurityContext() {
if (SecIsValidHandle(&ctxt_)) {
library_->DeleteSecurityContext(&ctxt_);
SecInvalidateHandle(&ctxt_);
}
}
HttpAuth::AuthorizationResult HttpAuthSSPI::ParseChallenge(
HttpAuthChallengeTokenizer* tok) {
if (!LowerCaseEqualsASCII(tok->scheme(), StringToLowerASCII(scheme_).c_str()))
return HttpAuth::AUTHORIZATION_RESULT_INVALID;
std::string encoded_auth_token = tok->base64_param();
if (encoded_auth_token.empty()) {
if (SecIsValidHandle(&ctxt_))
return HttpAuth::AUTHORIZATION_RESULT_REJECT;
DCHECK(decoded_server_auth_token_.empty());
return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
} else {
if (!SecIsValidHandle(&ctxt_))
return HttpAuth::AUTHORIZATION_RESULT_INVALID;
}
std::string decoded_auth_token;
bool base64_rv = base::Base64Decode(encoded_auth_token, &decoded_auth_token);
if (!base64_rv)
return HttpAuth::AUTHORIZATION_RESULT_INVALID;
decoded_server_auth_token_ = decoded_auth_token;
return HttpAuth::AUTHORIZATION_RESULT_ACCEPT;
}
int HttpAuthSSPI::GenerateAuthToken(const AuthCredentials* credentials,
const std::string& spn,
std::string* auth_token) {
if (!SecIsValidHandle(&cred_)) {
int rv = OnFirstRound(credentials);
if (rv != OK)
return rv;
}
DCHECK(SecIsValidHandle(&cred_));
void* out_buf;
int out_buf_len;
int rv = GetNextSecurityToken(
spn,
static_cast<void *>(const_cast<char *>(
decoded_server_auth_token_.c_str())),
decoded_server_auth_token_.length(),
&out_buf,
&out_buf_len);
if (rv != OK)
return rv;
std::string encode_input(static_cast<char*>(out_buf), out_buf_len);
std::string encode_output;
base::Base64Encode(encode_input, &encode_output);
free(out_buf);
*auth_token = scheme_ + " " + encode_output;
return OK;
}
int HttpAuthSSPI::OnFirstRound(const AuthCredentials* credentials) {
DCHECK(!SecIsValidHandle(&cred_));
int rv = OK;
if (credentials) {
base::string16 domain;
base::string16 user;
SplitDomainAndUser(credentials->username(), &domain, &user);
rv = AcquireExplicitCredentials(library_, security_package_, domain,
user, credentials->password(), &cred_);
if (rv != OK)
return rv;
} else {
rv = AcquireDefaultCredentials(library_, security_package_, &cred_);
if (rv != OK)
return rv;
}
return rv;
}
int HttpAuthSSPI::GetNextSecurityToken(
const std::string& spn,
const void* in_token,
int in_token_len,
void** out_token,
int* out_token_len) {
CtxtHandle* ctxt_ptr;
SecBufferDesc in_buffer_desc, out_buffer_desc;
SecBufferDesc* in_buffer_desc_ptr;
SecBuffer in_buffer, out_buffer;
if (in_token_len > 0) {
in_buffer_desc.ulVersion = SECBUFFER_VERSION;
in_buffer_desc.cBuffers = 1;
in_buffer_desc.pBuffers = &in_buffer;
in_buffer.BufferType = SECBUFFER_TOKEN;
in_buffer.cbBuffer = in_token_len;
in_buffer.pvBuffer = const_cast<void*>(in_token);
ctxt_ptr = &ctxt_;
in_buffer_desc_ptr = &in_buffer_desc;
} else {
if (SecIsValidHandle(&ctxt_)) {
NOTREACHED();
return ERR_UNEXPECTED;
}
ctxt_ptr = NULL;
in_buffer_desc_ptr = NULL;
}
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;
if (can_delegate_)
context_flags |= (ISC_REQ_DELEGATE | ISC_REQ_MUTUAL_AUTH);
DWORD context_attribute;
std::wstring spn_wide = base::ASCIIToWide(spn);
SECURITY_STATUS status = library_->InitializeSecurityContext(
&cred_,
ctxt_ptr,
const_cast<wchar_t *>(spn_wide.c_str()),
context_flags,
0,
SECURITY_NATIVE_DREP,
in_buffer_desc_ptr,
0,
&ctxt_,
&out_buffer_desc,
&context_attribute,
NULL);
int rv = MapInitializeSecurityContextStatusToError(status);
if (rv != OK) {
ResetSecurityContext();
free(out_buffer.pvBuffer);
return rv;
}
if (!out_buffer.cbBuffer) {
free(out_buffer.pvBuffer);
out_buffer.pvBuffer = NULL;
}
*out_token = out_buffer.pvBuffer;
*out_token_len = out_buffer.cbBuffer;
return OK;
}
void SplitDomainAndUser(const base::string16& combined,
base::string16* domain,
base::string16* user) {
size_t backslash_idx = combined.find(L'\\');
if (backslash_idx == base::string16::npos) {
domain->clear();
*user = combined;
} else {
*domain = combined.substr(0, backslash_idx);
*user = combined.substr(backslash_idx + 1);
}
}
int DetermineMaxTokenLength(SSPILibrary* library,
const std::wstring& package,
ULONG* max_token_length) {
DCHECK(library);
DCHECK(max_token_length);
PSecPkgInfo pkg_info = NULL;
SECURITY_STATUS status = library->QuerySecurityPackageInfo(
const_cast<wchar_t *>(package.c_str()), &pkg_info);
int rv = MapQuerySecurityPackageInfoStatusToError(status);
if (rv != OK)
return rv;
int token_length = pkg_info->cbMaxToken;
status = library->FreeContextBuffer(pkg_info);
rv = MapFreeContextBufferStatusToError(status);
if (rv != OK)
return rv;
*max_token_length = token_length;
return OK;
}
}