blob: b94c0b0e1f7b24264127d7e46e071261cb2a3434 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/credential_provider/gaiacp/scoped_lsa_policy.h"
#include <Windows.h> // Needed for ACCESS_MASK, <lm.h>
#include <Winternl.h>
#include <lm.h> // Needed for LSA_UNICODE_STRING
#define _NTDEF_ // Prevent redefition errors, must come after <winternl.h>
#include <ntsecapi.h> // For LSA_xxx types
#include "chrome/credential_provider/gaiacp/gcp_utils.h" // For STATUS_SUCCESS.
#include "chrome/credential_provider/gaiacp/logging.h"
namespace credential_provider {
// static
ScopedLsaPolicy::CreatorCallback* ScopedLsaPolicy::GetCreatorCallbackStorage() {
static CreatorCallback creator_for_testing;
return &creator_for_testing;
}
// static
void ScopedLsaPolicy::SetCreatorForTesting(CreatorCallback creator) {
*GetCreatorCallbackStorage() = creator;
}
// static
std::unique_ptr<ScopedLsaPolicy> ScopedLsaPolicy::Create(ACCESS_MASK mask) {
if (!GetCreatorCallbackStorage()->is_null())
return GetCreatorCallbackStorage()->Run(mask);
std::unique_ptr<ScopedLsaPolicy> scoped(new ScopedLsaPolicy(mask));
return scoped->IsValid() ? std::move(scoped) : nullptr;
}
ScopedLsaPolicy::ScopedLsaPolicy(ACCESS_MASK mask) {
LSA_OBJECT_ATTRIBUTES oa;
memset(&oa, 0, sizeof(oa));
NTSTATUS sts = ::LsaOpenPolicy(nullptr, &oa, mask, &handle_);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaOpenPolicy hr=" << putHR(hr);
::SetLastError(hr);
handle_ = nullptr;
}
}
ScopedLsaPolicy::~ScopedLsaPolicy() {
if (handle_ != nullptr)
::LsaClose(handle_);
}
bool ScopedLsaPolicy::IsValid() const {
return handle_ != nullptr;
}
HRESULT ScopedLsaPolicy::StorePrivateData(const wchar_t* key,
const wchar_t* value) {
LSA_UNICODE_STRING lsa_key;
InitLsaString(key, &lsa_key);
LSA_UNICODE_STRING lsa_value;
InitLsaString(value, &lsa_value);
// When calling LsaStorePrivateData(), the value's length should include
// the null terminator.
lsa_value.Length = lsa_value.MaximumLength;
NTSTATUS sts = ::LsaStorePrivateData(handle_, &lsa_key, &lsa_value);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaStorePrivateData hr=" << putHR(hr);
return hr;
}
return S_OK;
}
HRESULT ScopedLsaPolicy::RemovePrivateData(const wchar_t* key) {
LSA_UNICODE_STRING lsa_key;
InitLsaString(key, &lsa_key);
NTSTATUS sts = ::LsaStorePrivateData(handle_, &lsa_key, nullptr);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaStorePrivateData hr=" << putHR(hr);
return hr;
}
return S_OK;
}
HRESULT ScopedLsaPolicy::RetrievePrivateData(const wchar_t* key,
wchar_t* value,
size_t length) {
LSA_UNICODE_STRING lsa_key;
InitLsaString(key, &lsa_key);
LSA_UNICODE_STRING* lsa_value;
NTSTATUS sts = ::LsaRetrievePrivateData(handle_, &lsa_key, &lsa_value);
if (sts != STATUS_SUCCESS)
return HRESULT_FROM_NT(sts);
errno_t err = wcscpy_s(value, length, lsa_value->Buffer);
::LsaFreeMemory(lsa_value);
return err == 0 ? S_OK : E_FAIL;
}
HRESULT ScopedLsaPolicy::AddAccountRights(PSID sid, const wchar_t* right) {
LSA_UNICODE_STRING lsa_right;
InitLsaString(right, &lsa_right);
NTSTATUS sts = ::LsaAddAccountRights(handle_, sid, &lsa_right, 1);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaAddAccountRights sts=" << putHR(sts)
<< " hr=" << putHR(hr);
return hr;
}
return S_OK;
}
HRESULT ScopedLsaPolicy::RemoveAccountRights(PSID sid, const wchar_t* right) {
LSA_UNICODE_STRING lsa_right;
InitLsaString(right, &lsa_right);
NTSTATUS sts = ::LsaRemoveAccountRights(handle_, sid, FALSE, &lsa_right, 1);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaRemoveAccountRights sts=" << putHR(sts)
<< " hr=" << putHR(hr);
return hr;
}
return S_OK;
}
HRESULT ScopedLsaPolicy::RemoveAccount(PSID sid) {
// When all rights are removed from an account, the account itself is also
// deleted.
// I thought the above meant the user would be removed from the
// computer, but apparently I am mistaken. It is still important to call
// NetUserDel().
NTSTATUS sts = ::LsaRemoveAccountRights(handle_, sid, TRUE, nullptr, 0);
if (sts != STATUS_SUCCESS) {
HRESULT hr = HRESULT_FROM_NT(sts);
LOGFN(ERROR) << "LsaRemoveAccountRights sts=" << putHR(sts)
<< " hr=" << putHR(hr);
return hr;
}
return S_OK;
}
// static
void ScopedLsaPolicy::InitLsaString(const wchar_t* string,
_UNICODE_STRING* lsa_string) {
lsa_string->Buffer = const_cast<wchar_t*>(string);
lsa_string->Length =
static_cast<USHORT>(wcslen(lsa_string->Buffer) * sizeof(wchar_t));
lsa_string->MaximumLength = lsa_string->Length + sizeof(wchar_t);
}
} // namespace credential_provider