917 lines
37 KiB
C++
917 lines
37 KiB
C++
|
|
// Copyright 2021 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "base/win/access_token.h"
|
||
|
|
|
||
|
|
#include <windows.h>
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cstdint>
|
||
|
|
#include <map>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "base/win/atl.h"
|
||
|
|
#include "base/win/scoped_handle.h"
|
||
|
|
#include "base/win/security_util.h"
|
||
|
|
#include "base/win/windows_version.h"
|
||
|
|
#include "testing/gtest/include/gtest/gtest.h"
|
||
|
|
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||
|
|
|
||
|
|
namespace base::win {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
// All token access minus TOKEN_QUERY
|
||
|
|
constexpr ACCESS_MASK kTokenAllNoQuery = (TOKEN_ALL_ACCESS & ~TOKEN_QUERY);
|
||
|
|
|
||
|
|
bool CompareLuid(const CHROME_LUID& left, const LUID& right) {
|
||
|
|
return left.HighPart == right.HighPart && left.LowPart == right.LowPart;
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t ConvertLuid(const AccessToken::Privilege& priv) {
|
||
|
|
CHROME_LUID luid = priv.GetLuid();
|
||
|
|
return (static_cast<int64_t>(luid.HighPart) << 32) | luid.LowPart;
|
||
|
|
}
|
||
|
|
|
||
|
|
int64_t ConvertLuid(const LUID& luid) {
|
||
|
|
return (static_cast<int64_t>(luid.HighPart) << 32) | luid.LowPart;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EqualSid(const Sid& left, const ATL::CSid& right) {
|
||
|
|
return left.Equal(const_cast<SID*>(right.GetPSID()));
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareGroups(const std::vector<AccessToken::Group>& groups,
|
||
|
|
const ATL::CSid::CSidArray& sids,
|
||
|
|
const ATL::CAtlArray<DWORD>& attrs) {
|
||
|
|
ASSERT_EQ(groups.size(), sids.GetCount());
|
||
|
|
ASSERT_EQ(sids.GetCount(), attrs.GetCount());
|
||
|
|
std::map<std::wstring, DWORD> group_map;
|
||
|
|
for (const AccessToken::Group& group : groups) {
|
||
|
|
absl::optional<std::wstring> sddl = group.GetSid().ToSddlString();
|
||
|
|
ASSERT_TRUE(sddl);
|
||
|
|
group_map.insert({*sddl, group.GetAttributes()});
|
||
|
|
}
|
||
|
|
for (size_t index = 0; index < sids.GetCount(); ++index) {
|
||
|
|
auto found_group = group_map.find(sids[index].Sid());
|
||
|
|
ASSERT_NE(found_group, group_map.end());
|
||
|
|
EXPECT_EQ(found_group->second, attrs[index]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareGroups(const std::vector<AccessToken::Group>& groups,
|
||
|
|
const CTokenGroups& atl_groups) {
|
||
|
|
ATL::CSid::CSidArray sids;
|
||
|
|
ATL::CAtlArray<DWORD> attrs;
|
||
|
|
atl_groups.GetSidsAndAttributes(&sids, &attrs);
|
||
|
|
CompareGroups(groups, sids, attrs);
|
||
|
|
}
|
||
|
|
|
||
|
|
void ComparePrivileges(const std::vector<AccessToken::Privilege>& privs,
|
||
|
|
const ATL::CTokenPrivileges& atl_privs) {
|
||
|
|
CLUIDArray luids;
|
||
|
|
ATL::CTokenPrivileges::CAttributes attrs;
|
||
|
|
atl_privs.GetLuidsAndAttributes(&luids, &attrs);
|
||
|
|
ATL::CTokenPrivileges::CNames names;
|
||
|
|
atl_privs.GetNamesAndAttributes(&names);
|
||
|
|
ASSERT_EQ(privs.size(), luids.GetCount());
|
||
|
|
ASSERT_EQ(privs.size(), attrs.GetCount());
|
||
|
|
ASSERT_EQ(privs.size(), names.GetCount());
|
||
|
|
|
||
|
|
std::map<int64_t, const AccessToken::Privilege&> priv_map;
|
||
|
|
for (const AccessToken::Privilege& priv : privs) {
|
||
|
|
priv_map.insert({ConvertLuid(priv), priv});
|
||
|
|
}
|
||
|
|
|
||
|
|
for (size_t index = 0; index < luids.GetCount(); ++index) {
|
||
|
|
auto found_priv = priv_map.find(ConvertLuid(luids[index]));
|
||
|
|
ASSERT_NE(found_priv, priv_map.end());
|
||
|
|
EXPECT_TRUE(CompareLuid(found_priv->second.GetLuid(), luids[index]));
|
||
|
|
EXPECT_EQ(found_priv->second.GetAttributes(), attrs[index]);
|
||
|
|
EXPECT_EQ(found_priv->second.GetName().c_str(), names[index]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareIntegrityLevel(const AccessToken& token,
|
||
|
|
const ATL::CAccessToken& atl_token) {
|
||
|
|
char buffer[sizeof(TOKEN_MANDATORY_LABEL) + SECURITY_MAX_SID_SIZE];
|
||
|
|
DWORD size = sizeof(buffer);
|
||
|
|
ASSERT_TRUE(::GetTokenInformation(atl_token.GetHandle(), TokenIntegrityLevel,
|
||
|
|
buffer, size, &size));
|
||
|
|
TOKEN_MANDATORY_LABEL* label =
|
||
|
|
reinterpret_cast<TOKEN_MANDATORY_LABEL*>(buffer);
|
||
|
|
ASSERT_TRUE(label->Label.Sid);
|
||
|
|
absl::optional<Sid> il_sid = Sid::FromIntegrityLevel(token.IntegrityLevel());
|
||
|
|
ASSERT_TRUE(il_sid);
|
||
|
|
EXPECT_TRUE(il_sid->Equal(label->Label.Sid));
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareElevated(const AccessToken& token,
|
||
|
|
const ATL::CAccessToken& atl_token) {
|
||
|
|
TOKEN_ELEVATION elevation;
|
||
|
|
DWORD size = sizeof(elevation);
|
||
|
|
ASSERT_TRUE(::GetTokenInformation(atl_token.GetHandle(), TokenElevation,
|
||
|
|
&elevation, size, &size));
|
||
|
|
EXPECT_EQ(token.IsElevated(), !!elevation.TokenIsElevated);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool GetLinkedToken(const ATL::CAccessToken& token,
|
||
|
|
ATL::CAccessToken* linked_token) {
|
||
|
|
TOKEN_LINKED_TOKEN value;
|
||
|
|
DWORD size = sizeof(value);
|
||
|
|
if (!::GetTokenInformation(token.GetHandle(), TokenLinkedToken, &value, size,
|
||
|
|
&size)) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
linked_token->Attach(value.LinkedToken);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareDefaultDacl(const AccessToken& token,
|
||
|
|
const ATL::CAccessToken& atl_token) {
|
||
|
|
CDacl atl_dacl;
|
||
|
|
ASSERT_TRUE(atl_token.GetDefaultDacl(&atl_dacl));
|
||
|
|
ATL::CSid::CSidArray sids;
|
||
|
|
CAcl::CAccessMaskArray access;
|
||
|
|
CAcl::CAceTypeArray types;
|
||
|
|
CAcl::CAceFlagArray flags;
|
||
|
|
atl_dacl.GetAclEntries(&sids, &access, &types, &flags);
|
||
|
|
absl::optional<AccessControlList> dacl = token.DefaultDacl();
|
||
|
|
ASSERT_TRUE(dacl);
|
||
|
|
ACL* acl_ptr = dacl->get();
|
||
|
|
ASSERT_TRUE(acl_ptr);
|
||
|
|
DWORD ace_count = acl_ptr->AceCount;
|
||
|
|
ASSERT_EQ(ace_count, sids.GetCount());
|
||
|
|
ASSERT_EQ(ace_count, access.GetCount());
|
||
|
|
ASSERT_EQ(ace_count, types.GetCount());
|
||
|
|
ASSERT_EQ(ace_count, flags.GetCount());
|
||
|
|
for (DWORD index = 0; index < ace_count; ++index) {
|
||
|
|
ACE_HEADER* ace_header;
|
||
|
|
ASSERT_TRUE(GetAce(acl_ptr, index, reinterpret_cast<LPVOID*>(&ace_header)));
|
||
|
|
ASSERT_EQ(ace_header->AceType, types[index]);
|
||
|
|
ASSERT_EQ(ace_header->AceFlags, flags[index]);
|
||
|
|
// We only do a full comparison for these types of ACE.
|
||
|
|
if (ace_header->AceType == ACCESS_ALLOWED_ACE_TYPE ||
|
||
|
|
ace_header->AceType == ACCESS_DENIED_ACE_TYPE) {
|
||
|
|
// ACCESS_ALLOWED_ACE and ACCESS_DENIED_ACE have the same layout.
|
||
|
|
ACCESS_ALLOWED_ACE* ace =
|
||
|
|
reinterpret_cast<ACCESS_ALLOWED_ACE*>(ace_header);
|
||
|
|
EXPECT_EQ(ace->Mask, access[index]);
|
||
|
|
absl::optional<Sid> sid = Sid::FromPSID(&ace->SidStart);
|
||
|
|
ASSERT_TRUE(sid);
|
||
|
|
EXPECT_TRUE(EqualSid(*sid, sids[index]));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void CompareTokens(const AccessToken& token,
|
||
|
|
const ATL::CAccessToken& atl_token,
|
||
|
|
bool compare_linked_token = true) {
|
||
|
|
LUID luid;
|
||
|
|
// Only compare IDs if we're not comparing a token's linked token as the ID
|
||
|
|
// will be different.
|
||
|
|
if (compare_linked_token) {
|
||
|
|
ASSERT_TRUE(atl_token.GetTokenId(&luid));
|
||
|
|
EXPECT_TRUE(CompareLuid(token.Id(), luid));
|
||
|
|
}
|
||
|
|
ASSERT_TRUE(atl_token.GetLogonSessionId(&luid));
|
||
|
|
EXPECT_TRUE(CompareLuid(token.AuthenticationId(), luid));
|
||
|
|
ATL::CSid user_sid;
|
||
|
|
ASSERT_TRUE(atl_token.GetUser(&user_sid));
|
||
|
|
EXPECT_TRUE(EqualSid(token.User(), user_sid));
|
||
|
|
AccessToken::Group user_group = token.UserGroup();
|
||
|
|
EXPECT_TRUE(EqualSid(user_group.GetSid(), user_sid));
|
||
|
|
EXPECT_EQ(0U, user_group.GetAttributes());
|
||
|
|
ATL::CSid owner_sid;
|
||
|
|
ASSERT_TRUE(atl_token.GetOwner(&owner_sid));
|
||
|
|
EXPECT_TRUE(EqualSid(token.Owner(), owner_sid));
|
||
|
|
ATL::CSid primary_group;
|
||
|
|
ASSERT_TRUE(atl_token.GetPrimaryGroup(&primary_group));
|
||
|
|
EXPECT_TRUE(EqualSid(token.PrimaryGroup(), primary_group));
|
||
|
|
absl::optional<Sid> logon_sid = token.LogonId();
|
||
|
|
if (!logon_sid) {
|
||
|
|
EXPECT_EQ(DWORD{ERROR_NOT_FOUND}, ::GetLastError());
|
||
|
|
}
|
||
|
|
ATL::CSid atl_logon_sid;
|
||
|
|
if (!atl_token.GetLogonSid(&atl_logon_sid)) {
|
||
|
|
EXPECT_FALSE(logon_sid);
|
||
|
|
} else {
|
||
|
|
ASSERT_TRUE(logon_sid);
|
||
|
|
EXPECT_TRUE(EqualSid(*logon_sid, atl_logon_sid));
|
||
|
|
}
|
||
|
|
DWORD session_id;
|
||
|
|
ASSERT_TRUE(atl_token.GetTerminalServicesSessionId(&session_id));
|
||
|
|
EXPECT_EQ(token.SessionId(), session_id);
|
||
|
|
CompareIntegrityLevel(token, atl_token);
|
||
|
|
CompareElevated(token, atl_token);
|
||
|
|
EXPECT_EQ(token.IsRestricted(), atl_token.IsTokenRestricted());
|
||
|
|
TOKEN_TYPE token_type;
|
||
|
|
ASSERT_TRUE(atl_token.GetType(&token_type));
|
||
|
|
EXPECT_EQ(token.IsImpersonation(), token_type == TokenImpersonation);
|
||
|
|
if (token_type == TokenImpersonation) {
|
||
|
|
SECURITY_IMPERSONATION_LEVEL imp_level;
|
||
|
|
ASSERT_TRUE(atl_token.GetImpersonationLevel(&imp_level));
|
||
|
|
EXPECT_EQ(static_cast<int>(token.ImpersonationLevel()), imp_level);
|
||
|
|
}
|
||
|
|
CTokenGroups atl_groups;
|
||
|
|
ASSERT_TRUE(atl_token.GetGroups(&atl_groups));
|
||
|
|
CompareGroups(token.Groups(), atl_groups);
|
||
|
|
ATL::CTokenPrivileges atl_privs;
|
||
|
|
ASSERT_TRUE(atl_token.GetPrivileges(&atl_privs));
|
||
|
|
ComparePrivileges(token.Privileges(), atl_privs);
|
||
|
|
CompareDefaultDacl(token, atl_token);
|
||
|
|
absl::optional<AccessToken> linked_token = token.LinkedToken();
|
||
|
|
ATL::CAccessToken atl_linked_token;
|
||
|
|
bool result = GetLinkedToken(atl_token, &atl_linked_token);
|
||
|
|
if (!linked_token) {
|
||
|
|
EXPECT_FALSE(result);
|
||
|
|
} else {
|
||
|
|
ASSERT_TRUE(result);
|
||
|
|
if (compare_linked_token)
|
||
|
|
CompareTokens(*linked_token, atl_linked_token, false);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool DuplicateTokenWithSecurityDescriptor(const ATL::CAccessToken& token,
|
||
|
|
DWORD desired_access,
|
||
|
|
LPCWSTR security_descriptor,
|
||
|
|
ATL::CAccessToken* new_token) {
|
||
|
|
ATL::CSecurityDesc sd;
|
||
|
|
if (!sd.FromString(security_descriptor))
|
||
|
|
return false;
|
||
|
|
ATL::CSecurityAttributes sa;
|
||
|
|
sa.Set(sd);
|
||
|
|
return token.CreatePrimaryToken(new_token, desired_access, &sa);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool CreateImpersonationToken(SECURITY_IMPERSONATION_LEVEL impersonation_level,
|
||
|
|
ATL::CAccessToken* imp_token) {
|
||
|
|
ATL::CAccessToken token;
|
||
|
|
if (!token.GetProcessToken(MAXIMUM_ALLOWED))
|
||
|
|
return false;
|
||
|
|
return token.CreateImpersonationToken(imp_token, impersonation_level);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CheckTokenError(const absl::optional<AccessToken>& token,
|
||
|
|
DWORD expected_error) {
|
||
|
|
DWORD error = ::GetLastError();
|
||
|
|
EXPECT_FALSE(token);
|
||
|
|
EXPECT_EQ(expected_error, error);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CheckError(bool result, DWORD expected_error) {
|
||
|
|
DWORD error = ::GetLastError();
|
||
|
|
EXPECT_FALSE(result);
|
||
|
|
EXPECT_EQ(expected_error, error);
|
||
|
|
}
|
||
|
|
|
||
|
|
void CheckAccessTokenGroup(DWORD attributes,
|
||
|
|
bool integrity,
|
||
|
|
bool enabled,
|
||
|
|
bool deny_only,
|
||
|
|
bool logon_id) {
|
||
|
|
AccessToken::Group group(Sid(WellKnownSid::kWorld), attributes);
|
||
|
|
EXPECT_EQ(L"S-1-1-0", *group.GetSid().ToSddlString());
|
||
|
|
EXPECT_EQ(integrity, group.IsIntegrity());
|
||
|
|
EXPECT_EQ(enabled, group.IsEnabled());
|
||
|
|
EXPECT_EQ(deny_only, group.IsDenyOnly());
|
||
|
|
EXPECT_EQ(logon_id, group.IsLogonId());
|
||
|
|
}
|
||
|
|
|
||
|
|
void CheckAccessTokenPrivilege(LPCWSTR name, DWORD attributes, bool enabled) {
|
||
|
|
LUID luid;
|
||
|
|
ASSERT_TRUE(::LookupPrivilegeValue(nullptr, name, &luid));
|
||
|
|
CHROME_LUID chrome_luid;
|
||
|
|
chrome_luid.LowPart = luid.LowPart;
|
||
|
|
chrome_luid.HighPart = luid.HighPart;
|
||
|
|
AccessToken::Privilege priv(chrome_luid, attributes);
|
||
|
|
EXPECT_TRUE(CompareLuid(priv.GetLuid(), luid));
|
||
|
|
EXPECT_EQ(name, priv.GetName());
|
||
|
|
EXPECT_EQ(attributes, priv.GetAttributes());
|
||
|
|
EXPECT_EQ(enabled, priv.IsEnabled());
|
||
|
|
}
|
||
|
|
|
||
|
|
typedef NTSTATUS(WINAPI* NtCreateLowBoxToken)(OUT PHANDLE token,
|
||
|
|
IN HANDLE original_handle,
|
||
|
|
IN ACCESS_MASK access,
|
||
|
|
IN PVOID object_attribute,
|
||
|
|
IN PSID appcontainer_sid,
|
||
|
|
IN DWORD capabilityCount,
|
||
|
|
IN PSID_AND_ATTRIBUTES
|
||
|
|
capabilities,
|
||
|
|
IN DWORD handle_count,
|
||
|
|
IN PHANDLE handles);
|
||
|
|
|
||
|
|
void CompareAppContainer(const Sid& package_sid, const std::vector<Sid>& caps) {
|
||
|
|
static NtCreateLowBoxToken pNtCreateLowBoxToken =
|
||
|
|
reinterpret_cast<NtCreateLowBoxToken>(::GetProcAddress(
|
||
|
|
::GetModuleHandle(L"ntdll.dll"), "NtCreateLowBoxToken"));
|
||
|
|
ASSERT_TRUE(pNtCreateLowBoxToken);
|
||
|
|
ATL::CTokenGroups capabilities;
|
||
|
|
for (const Sid& cap : caps) {
|
||
|
|
capabilities.Add(ATL::CSid(static_cast<SID*>(cap.GetPSID())),
|
||
|
|
SE_GROUP_ENABLED);
|
||
|
|
}
|
||
|
|
ATL::CAccessToken process_token;
|
||
|
|
ASSERT_TRUE(process_token.GetProcessToken(TOKEN_ALL_ACCESS));
|
||
|
|
UINT cap_count = capabilities.GetCount();
|
||
|
|
PTOKEN_GROUPS cap_groups =
|
||
|
|
const_cast<PTOKEN_GROUPS>(capabilities.GetPTOKEN_GROUPS());
|
||
|
|
HANDLE tmp_token;
|
||
|
|
NTSTATUS status = pNtCreateLowBoxToken(
|
||
|
|
&tmp_token, process_token.GetHandle(), TOKEN_ALL_ACCESS, nullptr,
|
||
|
|
package_sid.GetPSID(), cap_count,
|
||
|
|
cap_count > 0 ? cap_groups->Groups : nullptr, 0, nullptr);
|
||
|
|
ASSERT_EQ(0, status);
|
||
|
|
ScopedHandle scoped_tmp_token(tmp_token);
|
||
|
|
absl::optional<AccessToken> ac_token =
|
||
|
|
AccessToken::FromToken(scoped_tmp_token.get());
|
||
|
|
ASSERT_TRUE(ac_token);
|
||
|
|
EXPECT_TRUE(ac_token->IsAppContainer());
|
||
|
|
EXPECT_EQ(ac_token->AppContainerSid(), package_sid);
|
||
|
|
CompareGroups(ac_token->Capabilities(), capabilities);
|
||
|
|
}
|
||
|
|
|
||
|
|
ACCESS_MASK GetTokenAccess(const AccessToken& token) {
|
||
|
|
absl::optional<ACCESS_MASK> granted_access = GetGrantedAccess(token.get());
|
||
|
|
CHECK(granted_access);
|
||
|
|
return *granted_access;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<Sid> GroupsToSids(const std::vector<AccessToken::Group>& groups) {
|
||
|
|
std::vector<Sid> sids;
|
||
|
|
for (const AccessToken::Group& group : groups) {
|
||
|
|
sids.push_back(group.GetSid().Clone());
|
||
|
|
}
|
||
|
|
return sids;
|
||
|
|
}
|
||
|
|
|
||
|
|
std::vector<std::wstring> PrivilegesToNames(
|
||
|
|
const std::vector<AccessToken::Privilege>& privs) {
|
||
|
|
std::vector<std::wstring> sids;
|
||
|
|
for (const AccessToken::Privilege& priv : privs) {
|
||
|
|
sids.push_back(priv.GetName());
|
||
|
|
}
|
||
|
|
return sids;
|
||
|
|
}
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromToken) {
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_QUERY));
|
||
|
|
|
||
|
|
absl::optional<AccessToken> token =
|
||
|
|
AccessToken::FromToken(atl_token.GetHandle());
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
CompareTokens(*token, atl_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*token), DWORD{TOKEN_QUERY});
|
||
|
|
|
||
|
|
absl::optional<AccessToken> all_access_token =
|
||
|
|
AccessToken::FromToken(atl_token.GetHandle(), kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
// Check that we duplicated the handle.
|
||
|
|
LUID luid;
|
||
|
|
ASSERT_TRUE(atl_token.GetTokenId(&luid));
|
||
|
|
::CloseHandle(atl_token.Detach());
|
||
|
|
LUID temp_luid;
|
||
|
|
ASSERT_FALSE(atl_token.GetTokenId(&temp_luid));
|
||
|
|
EXPECT_TRUE(CompareLuid(token->Id(), luid));
|
||
|
|
|
||
|
|
// Check that we duplicate with the correct access rights.
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_QUERY_SOURCE));
|
||
|
|
ASSERT_FALSE(atl_token.GetTokenId(&temp_luid));
|
||
|
|
absl::optional<AccessToken> token2 =
|
||
|
|
AccessToken::FromToken(atl_token.GetHandle());
|
||
|
|
ASSERT_TRUE(token2);
|
||
|
|
EXPECT_TRUE(CompareLuid(token2->Id(), luid));
|
||
|
|
|
||
|
|
// Check that we fail if we don't have access to the token object.
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_DUPLICATE));
|
||
|
|
ATL::CAccessToken pri_token;
|
||
|
|
ASSERT_TRUE(DuplicateTokenWithSecurityDescriptor(
|
||
|
|
atl_token, TOKEN_QUERY_SOURCE, L"D:", &pri_token));
|
||
|
|
CheckTokenError(AccessToken::FromToken(pri_token.GetHandle()),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
|
||
|
|
CheckTokenError(AccessToken::FromToken(ScopedHandle(nullptr)),
|
||
|
|
ERROR_INVALID_HANDLE);
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_QUERY));
|
||
|
|
ScopedHandle token_handle(atl_token.Detach());
|
||
|
|
token = AccessToken::FromToken(std::move(token_handle));
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
EXPECT_FALSE(token_handle.is_valid());
|
||
|
|
EXPECT_TRUE(token->is_valid());
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_DUPLICATE));
|
||
|
|
token_handle.Set(atl_token.Detach());
|
||
|
|
CheckTokenError(AccessToken::FromToken(std::move(token_handle)),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromProcess) {
|
||
|
|
ScopedHandle process(
|
||
|
|
::OpenProcess(PROCESS_TERMINATE, FALSE, ::GetCurrentProcessId()));
|
||
|
|
ASSERT_TRUE(process.is_valid());
|
||
|
|
CheckTokenError(AccessToken::FromProcess(process.get()), ERROR_ACCESS_DENIED);
|
||
|
|
CheckTokenError(AccessToken::FromProcess(process.get(), true),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
process.Set(::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE,
|
||
|
|
::GetCurrentProcessId()));
|
||
|
|
ASSERT_TRUE(process.is_valid());
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromProcess(process.get());
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*token), DWORD{TOKEN_QUERY});
|
||
|
|
ASSERT_FALSE(token->IsImpersonation());
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_QUERY, process.get()));
|
||
|
|
CompareTokens(*token, atl_token);
|
||
|
|
absl::optional<AccessToken> imp_token =
|
||
|
|
AccessToken::FromProcess(process.get(), true);
|
||
|
|
ASSERT_TRUE(imp_token);
|
||
|
|
ASSERT_TRUE(imp_token->IsImpersonation());
|
||
|
|
ASSERT_TRUE(imp_token->IsIdentification());
|
||
|
|
|
||
|
|
absl::optional<AccessToken> all_access_token =
|
||
|
|
AccessToken::FromProcess(process.get(), false, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
all_access_token =
|
||
|
|
AccessToken::FromProcess(process.get(), true, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromCurrentProcess) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
ASSERT_FALSE(token->IsImpersonation());
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(TOKEN_QUERY));
|
||
|
|
CompareTokens(*token, atl_token);
|
||
|
|
absl::optional<AccessToken> imp_token = AccessToken::FromCurrentProcess(true);
|
||
|
|
ASSERT_TRUE(imp_token);
|
||
|
|
ASSERT_TRUE(imp_token->IsImpersonation());
|
||
|
|
ASSERT_TRUE(imp_token->IsIdentification());
|
||
|
|
|
||
|
|
absl::optional<AccessToken> all_access_token =
|
||
|
|
AccessToken::FromCurrentProcess(false, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
all_access_token = AccessToken::FromCurrentProcess(true, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromThread) {
|
||
|
|
// Make sure we have no impersonation token before starting.
|
||
|
|
::RevertToSelf();
|
||
|
|
// Check we
|
||
|
|
CheckTokenError(AccessToken::FromThread(::GetCurrentThread()),
|
||
|
|
ERROR_NO_TOKEN);
|
||
|
|
ScopedHandle thread(
|
||
|
|
::OpenThread(THREAD_TERMINATE, FALSE, ::GetCurrentThreadId()));
|
||
|
|
ASSERT_TRUE(thread.is_valid());
|
||
|
|
CheckTokenError(AccessToken::FromThread(thread.get()), ERROR_ACCESS_DENIED);
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_imp_token;
|
||
|
|
ASSERT_TRUE(CreateImpersonationToken(SecurityImpersonation, &atl_imp_token));
|
||
|
|
ASSERT_TRUE(atl_imp_token.Impersonate());
|
||
|
|
CAutoRevertImpersonation scoped_imp(&atl_imp_token);
|
||
|
|
|
||
|
|
thread.Set(::OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE,
|
||
|
|
::GetCurrentThreadId()));
|
||
|
|
ASSERT_TRUE(thread.is_valid());
|
||
|
|
absl::optional<AccessToken> imp_token = AccessToken::FromThread(thread.get());
|
||
|
|
absl::optional<AccessToken> all_access_token =
|
||
|
|
AccessToken::FromThread(thread.get(), true, kTokenAllNoQuery);
|
||
|
|
atl_imp_token.Revert();
|
||
|
|
ASSERT_TRUE(imp_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*imp_token), DWORD{TOKEN_QUERY});
|
||
|
|
ASSERT_TRUE(imp_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(imp_token->IsIdentification());
|
||
|
|
CompareTokens(*imp_token, atl_imp_token);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_id_token;
|
||
|
|
ASSERT_TRUE(CreateImpersonationToken(SecurityIdentification, &atl_id_token));
|
||
|
|
ASSERT_TRUE(atl_id_token.Impersonate());
|
||
|
|
CAutoRevertImpersonation scoped_imp2(&atl_id_token);
|
||
|
|
CheckTokenError(AccessToken::FromThread(thread.get(), false),
|
||
|
|
ERROR_BAD_IMPERSONATION_LEVEL);
|
||
|
|
absl::optional<AccessToken> id_token =
|
||
|
|
AccessToken::FromThread(thread.get(), true);
|
||
|
|
atl_id_token.Revert();
|
||
|
|
ASSERT_TRUE(id_token);
|
||
|
|
EXPECT_TRUE(id_token->IsIdentification());
|
||
|
|
CompareTokens(*id_token, atl_id_token);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromCurrentThread) {
|
||
|
|
// Make sure we have no impersonation token before starting.
|
||
|
|
::RevertToSelf();
|
||
|
|
// Check we don't have a token.
|
||
|
|
CheckTokenError(AccessToken::FromCurrentThread(), ERROR_NO_TOKEN);
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_imp_token;
|
||
|
|
ASSERT_TRUE(CreateImpersonationToken(SecurityImpersonation, &atl_imp_token));
|
||
|
|
ASSERT_TRUE(atl_imp_token.Impersonate());
|
||
|
|
CAutoRevertImpersonation scoped_imp(&atl_imp_token);
|
||
|
|
|
||
|
|
absl::optional<AccessToken> imp_token = AccessToken::FromCurrentThread();
|
||
|
|
absl::optional<AccessToken> all_access_token =
|
||
|
|
AccessToken::FromCurrentThread(true, kTokenAllNoQuery);
|
||
|
|
atl_imp_token.Revert();
|
||
|
|
ASSERT_TRUE(imp_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*imp_token), DWORD{TOKEN_QUERY});
|
||
|
|
ASSERT_TRUE(imp_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(imp_token->IsIdentification());
|
||
|
|
CompareTokens(*imp_token, atl_imp_token);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_id_token;
|
||
|
|
ASSERT_TRUE(CreateImpersonationToken(SecurityIdentification, &atl_id_token));
|
||
|
|
ASSERT_TRUE(atl_id_token.Impersonate());
|
||
|
|
ATL::CAutoRevertImpersonation scoped_imp2(&atl_id_token);
|
||
|
|
CheckTokenError(AccessToken::FromCurrentThread(false),
|
||
|
|
ERROR_BAD_IMPERSONATION_LEVEL);
|
||
|
|
absl::optional<AccessToken> id_token = AccessToken::FromCurrentThread(true);
|
||
|
|
atl_id_token.Revert();
|
||
|
|
ASSERT_TRUE(id_token);
|
||
|
|
EXPECT_TRUE(id_token->IsIdentification());
|
||
|
|
CompareTokens(*id_token, atl_id_token);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, FromEffective) {
|
||
|
|
// Make sure we have no impersonation token before starting.
|
||
|
|
::RevertToSelf();
|
||
|
|
absl::optional<base::win::AccessToken> primary_token =
|
||
|
|
AccessToken::FromEffective();
|
||
|
|
absl::optional<base::win::AccessToken> all_access_token =
|
||
|
|
AccessToken::FromEffective(kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(primary_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*primary_token), DWORD{TOKEN_QUERY});
|
||
|
|
EXPECT_FALSE(primary_token->IsImpersonation());
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_primary_token;
|
||
|
|
ASSERT_TRUE(atl_primary_token.GetProcessToken(TOKEN_QUERY));
|
||
|
|
CompareTokens(*primary_token, atl_primary_token);
|
||
|
|
|
||
|
|
ATL::CAccessToken atl_imp_token;
|
||
|
|
ASSERT_TRUE(CreateImpersonationToken(SecurityImpersonation, &atl_imp_token));
|
||
|
|
ASSERT_TRUE(atl_imp_token.Impersonate());
|
||
|
|
CAutoRevertImpersonation scoped_imp(&atl_imp_token);
|
||
|
|
|
||
|
|
absl::optional<AccessToken> imp_token = AccessToken::FromEffective();
|
||
|
|
all_access_token = AccessToken::FromEffective(kTokenAllNoQuery);
|
||
|
|
atl_imp_token.Revert();
|
||
|
|
ASSERT_TRUE(imp_token);
|
||
|
|
ASSERT_TRUE(imp_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(imp_token->IsIdentification());
|
||
|
|
CompareTokens(*imp_token, atl_imp_token);
|
||
|
|
ASSERT_TRUE(all_access_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*all_access_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, AccessTokenGroup) {
|
||
|
|
CheckAccessTokenGroup(0, false, false, false, false);
|
||
|
|
CheckAccessTokenGroup(SE_GROUP_INTEGRITY, true, false, false, false);
|
||
|
|
CheckAccessTokenGroup(SE_GROUP_ENABLED, false, true, false, false);
|
||
|
|
CheckAccessTokenGroup(SE_GROUP_USE_FOR_DENY_ONLY, false, false, true, false);
|
||
|
|
CheckAccessTokenGroup(SE_GROUP_LOGON_ID, false, false, false, true);
|
||
|
|
CheckAccessTokenGroup(0xFFFFFFFF, true, true, true, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, AccessTokenPrivilege) {
|
||
|
|
CheckAccessTokenPrivilege(SE_DEBUG_NAME, 0, false);
|
||
|
|
CheckAccessTokenPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED_BY_DEFAULT,
|
||
|
|
false);
|
||
|
|
CheckAccessTokenPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED, true);
|
||
|
|
CheckAccessTokenPrivilege(
|
||
|
|
SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED | SE_PRIVILEGE_ENABLED_BY_DEFAULT,
|
||
|
|
true);
|
||
|
|
CheckAccessTokenPrivilege(SE_IMPERSONATE_NAME, 0, false);
|
||
|
|
CHROME_LUID luid{0x8181, 0x5656};
|
||
|
|
AccessToken::Privilege priv(luid, 0);
|
||
|
|
EXPECT_EQ(L"00005656-00008181", priv.GetName());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, IsMember) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
ASSERT_FALSE(token->IsImpersonation());
|
||
|
|
CheckError(token->IsMember(WellKnownSid::kWorld),
|
||
|
|
ERROR_NO_IMPERSONATION_TOKEN);
|
||
|
|
absl::optional<Sid> sid = Sid::FromSddlString(L"S-1-1-2-3-4-5-6-7-8");
|
||
|
|
ASSERT_TRUE(sid);
|
||
|
|
CheckError(token->IsMember(*sid), ERROR_NO_IMPERSONATION_TOKEN);
|
||
|
|
|
||
|
|
absl::optional<AccessToken> imp_token = AccessToken::FromCurrentProcess(true);
|
||
|
|
EXPECT_TRUE(imp_token->IsMember(WellKnownSid::kWorld));
|
||
|
|
EXPECT_FALSE(imp_token->IsMember(WellKnownSid::kNull));
|
||
|
|
EXPECT_TRUE(imp_token->IsMember(imp_token->User()));
|
||
|
|
EXPECT_FALSE(imp_token->IsMember(*sid));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, Restricted) {
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(MAXIMUM_ALLOWED));
|
||
|
|
ATL::CTokenGroups disable_groups;
|
||
|
|
disable_groups.Add(ATL::Sids::World(), 0);
|
||
|
|
ATL::CTokenGroups restrict_groups;
|
||
|
|
restrict_groups.Add(ATL::Sids::RestrictedCode(), 0);
|
||
|
|
restrict_groups.Add(ATL::Sids::Users(), 0);
|
||
|
|
ATL::CAccessToken atl_restricted;
|
||
|
|
ASSERT_TRUE(atl_token.CreateRestrictedToken(&atl_restricted, disable_groups,
|
||
|
|
restrict_groups));
|
||
|
|
absl::optional<AccessToken> restricted =
|
||
|
|
AccessToken::FromToken(atl_restricted.GetHandle());
|
||
|
|
ASSERT_TRUE(restricted);
|
||
|
|
EXPECT_TRUE(restricted->IsRestricted());
|
||
|
|
CompareTokens(*restricted, atl_restricted, false);
|
||
|
|
ATL::CSid::CSidArray sids;
|
||
|
|
restrict_groups.GetSidsAndAttributes(&sids);
|
||
|
|
ATL::CAtlArray<DWORD> attrs;
|
||
|
|
for (UINT index = 0; index < sids.GetCount(); ++index) {
|
||
|
|
attrs.Add(SE_GROUP_MANDATORY | SE_GROUP_ENABLED_BY_DEFAULT |
|
||
|
|
SE_GROUP_ENABLED);
|
||
|
|
}
|
||
|
|
CompareGroups(restricted->RestrictedSids(), sids, attrs);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, AppContainer) {
|
||
|
|
absl::optional<Sid> package_sid =
|
||
|
|
Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-7");
|
||
|
|
ASSERT_TRUE(package_sid);
|
||
|
|
absl::optional<std::vector<Sid>> caps =
|
||
|
|
Sid::FromKnownCapabilityVector({WellKnownCapability::kInternetClient,
|
||
|
|
WellKnownCapability::kDocumentsLibrary});
|
||
|
|
ASSERT_TRUE(caps);
|
||
|
|
|
||
|
|
CompareAppContainer(*package_sid, *caps);
|
||
|
|
CompareAppContainer(*package_sid, {});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, Anonymous) {
|
||
|
|
ATL::CAccessToken atl_anon_token;
|
||
|
|
ASSERT_TRUE(::ImpersonateAnonymousToken(::GetCurrentThread()));
|
||
|
|
bool result = atl_anon_token.GetThreadToken(TOKEN_ALL_ACCESS);
|
||
|
|
::RevertToSelf();
|
||
|
|
ASSERT_TRUE(result);
|
||
|
|
absl::optional<AccessToken> anon_token =
|
||
|
|
AccessToken::FromToken(atl_anon_token.GetHandle());
|
||
|
|
ASSERT_TRUE(anon_token);
|
||
|
|
CompareTokens(*anon_token, atl_anon_token);
|
||
|
|
EXPECT_EQ(Sid(WellKnownSid::kAnonymous), anon_token->User());
|
||
|
|
absl::optional<Sid> logon_sid = anon_token->LogonId();
|
||
|
|
EXPECT_FALSE(anon_token->LogonId());
|
||
|
|
EXPECT_EQ(DWORD{ERROR_NOT_FOUND}, ::GetLastError());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, SetDefaultDacl) {
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(MAXIMUM_ALLOWED));
|
||
|
|
ATL::CAccessToken atl_dup_token;
|
||
|
|
ASSERT_TRUE(atl_token.CreatePrimaryToken(&atl_dup_token));
|
||
|
|
absl::optional<AccessToken> read_only_token =
|
||
|
|
AccessToken::FromToken(atl_dup_token.GetHandle());
|
||
|
|
AccessControlList default_dacl;
|
||
|
|
Sid world_sid(WellKnownSid::kWorld);
|
||
|
|
ASSERT_TRUE(default_dacl.SetEntry(world_sid, SecurityAccessMode::kGrant,
|
||
|
|
GENERIC_ALL, 0));
|
||
|
|
EXPECT_FALSE(read_only_token->SetDefaultDacl(default_dacl));
|
||
|
|
absl::optional<AccessToken> token =
|
||
|
|
AccessToken::FromToken(atl_dup_token.GetHandle(), TOKEN_ADJUST_DEFAULT);
|
||
|
|
EXPECT_TRUE(token->SetDefaultDacl(default_dacl));
|
||
|
|
|
||
|
|
CDacl atl_dacl;
|
||
|
|
ASSERT_TRUE(atl_dup_token.GetDefaultDacl(&atl_dacl));
|
||
|
|
ASSERT_EQ(atl_dacl.GetAceCount(), 1U);
|
||
|
|
ATL::CSid::CSidArray sids;
|
||
|
|
CAcl::CAccessMaskArray access;
|
||
|
|
CAcl::CAceTypeArray types;
|
||
|
|
CAcl::CAceFlagArray flags;
|
||
|
|
atl_dacl.GetAclEntries(&sids, &access, &types, &flags);
|
||
|
|
EXPECT_TRUE(EqualSid(world_sid, sids[0]));
|
||
|
|
EXPECT_EQ(access[0], DWORD{GENERIC_ALL});
|
||
|
|
EXPECT_EQ(types[0], ACCESS_ALLOWED_ACE_TYPE);
|
||
|
|
EXPECT_EQ(flags[0], 0U);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, SetIntegrityLevel) {
|
||
|
|
ATL::CAccessToken atl_token;
|
||
|
|
ASSERT_TRUE(atl_token.GetProcessToken(MAXIMUM_ALLOWED));
|
||
|
|
ATL::CAccessToken atl_dup_token;
|
||
|
|
ASSERT_TRUE(atl_token.CreatePrimaryToken(&atl_dup_token));
|
||
|
|
absl::optional<AccessToken> read_only_token =
|
||
|
|
AccessToken::FromToken(atl_dup_token.GetHandle());
|
||
|
|
EXPECT_FALSE(
|
||
|
|
read_only_token->SetIntegrityLevel(SECURITY_MANDATORY_UNTRUSTED_RID));
|
||
|
|
absl::optional<AccessToken> token =
|
||
|
|
AccessToken::FromToken(atl_dup_token.GetHandle(), TOKEN_ADJUST_DEFAULT);
|
||
|
|
EXPECT_TRUE(token->SetIntegrityLevel(SECURITY_MANDATORY_LOW_RID));
|
||
|
|
EXPECT_EQ(token->IntegrityLevel(), DWORD{SECURITY_MANDATORY_LOW_RID});
|
||
|
|
EXPECT_TRUE(token->SetIntegrityLevel(SECURITY_MANDATORY_UNTRUSTED_RID));
|
||
|
|
EXPECT_EQ(token->IntegrityLevel(), DWORD{SECURITY_MANDATORY_UNTRUSTED_RID});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, DuplicatePrimary) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
CheckTokenError(token->DuplicatePrimary(), ERROR_ACCESS_DENIED);
|
||
|
|
token = AccessToken::FromCurrentProcess(false, TOKEN_DUPLICATE);
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
absl::optional<AccessToken> dup_token = token->DuplicatePrimary();
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_FALSE(dup_token->IsImpersonation());
|
||
|
|
EXPECT_EQ(GetTokenAccess(*dup_token), DWORD{TOKEN_QUERY});
|
||
|
|
dup_token = token->DuplicatePrimary(kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_FALSE(dup_token->IsImpersonation());
|
||
|
|
EXPECT_EQ(GetTokenAccess(*dup_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, DuplicateImpersonation) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
CheckTokenError(
|
||
|
|
token->DuplicateImpersonation(SecurityImpersonationLevel::kImpersonation),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
token = AccessToken::FromCurrentProcess(false, TOKEN_DUPLICATE);
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
absl::optional<AccessToken> dup_token = token->DuplicateImpersonation();
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_TRUE(dup_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(dup_token->IsIdentification());
|
||
|
|
EXPECT_EQ(dup_token->ImpersonationLevel(),
|
||
|
|
SecurityImpersonationLevel::kImpersonation);
|
||
|
|
dup_token =
|
||
|
|
token->DuplicateImpersonation(SecurityImpersonationLevel::kImpersonation);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_TRUE(dup_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(dup_token->IsIdentification());
|
||
|
|
EXPECT_EQ(dup_token->ImpersonationLevel(),
|
||
|
|
SecurityImpersonationLevel::kImpersonation);
|
||
|
|
dup_token =
|
||
|
|
token->DuplicateImpersonation(SecurityImpersonationLevel::kAnonymous);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_EQ(dup_token->ImpersonationLevel(),
|
||
|
|
SecurityImpersonationLevel::kAnonymous);
|
||
|
|
dup_token = token->DuplicateImpersonation(
|
||
|
|
SecurityImpersonationLevel::kIdentification);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_EQ(dup_token->ImpersonationLevel(),
|
||
|
|
SecurityImpersonationLevel::kIdentification);
|
||
|
|
dup_token =
|
||
|
|
token->DuplicateImpersonation(SecurityImpersonationLevel::kDelegation);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_EQ(dup_token->ImpersonationLevel(),
|
||
|
|
SecurityImpersonationLevel::kDelegation);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*dup_token), DWORD{TOKEN_QUERY});
|
||
|
|
dup_token = token->DuplicateImpersonation(
|
||
|
|
SecurityImpersonationLevel::kImpersonation, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
EXPECT_TRUE(dup_token->IsImpersonation());
|
||
|
|
EXPECT_FALSE(dup_token->IsIdentification());
|
||
|
|
EXPECT_EQ(GetTokenAccess(*dup_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
dup_token = token->DuplicateImpersonation(
|
||
|
|
SecurityImpersonationLevel::kIdentification, TOKEN_DUPLICATE);
|
||
|
|
ASSERT_TRUE(dup_token);
|
||
|
|
CheckTokenError(dup_token->DuplicateImpersonation(
|
||
|
|
SecurityImpersonationLevel::kImpersonation),
|
||
|
|
ERROR_BAD_IMPERSONATION_LEVEL);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, CreateRestricted) {
|
||
|
|
absl::optional<AccessToken> primary_token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(primary_token);
|
||
|
|
CheckTokenError(primary_token->CreateRestricted(0, {}, {}, {}),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
primary_token = AccessToken::FromCurrentProcess(false, TOKEN_ALL_ACCESS);
|
||
|
|
absl::optional<AccessToken> restricted_token =
|
||
|
|
primary_token->CreateRestricted(DISABLE_MAX_PRIVILEGE, {}, {}, {});
|
||
|
|
ASSERT_TRUE(restricted_token);
|
||
|
|
EXPECT_FALSE(restricted_token->IsRestricted());
|
||
|
|
EXPECT_EQ(GetTokenAccess(*restricted_token), DWORD{TOKEN_QUERY});
|
||
|
|
restricted_token = primary_token->CreateRestricted(DISABLE_MAX_PRIVILEGE, {},
|
||
|
|
{}, {}, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(restricted_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*restricted_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
std::vector<AccessToken::Privilege> privs = restricted_token->Privileges();
|
||
|
|
ASSERT_EQ(privs.size(), 1U);
|
||
|
|
EXPECT_EQ(privs[0].GetName(), SE_CHANGE_NOTIFY_NAME);
|
||
|
|
std::vector<std::wstring> priv_names = {L"ThisIsNotValid"};
|
||
|
|
EXPECT_FALSE(primary_token->CreateRestricted(0, {}, priv_names, {}));
|
||
|
|
restricted_token = primary_token->CreateRestricted(
|
||
|
|
0, {}, PrivilegesToNames(primary_token->Privileges()), {});
|
||
|
|
ASSERT_TRUE(restricted_token);
|
||
|
|
EXPECT_FALSE(restricted_token->IsRestricted());
|
||
|
|
EXPECT_TRUE(restricted_token->Privileges().empty());
|
||
|
|
std::vector<AccessToken::Group> groups = primary_token->Groups();
|
||
|
|
restricted_token =
|
||
|
|
primary_token->CreateRestricted(0, GroupsToSids(groups), {}, {});
|
||
|
|
ASSERT_TRUE(restricted_token);
|
||
|
|
EXPECT_FALSE(restricted_token->IsRestricted());
|
||
|
|
for (const AccessToken::Group& group : restricted_token->Groups()) {
|
||
|
|
if (!group.IsIntegrity()) {
|
||
|
|
EXPECT_TRUE(group.IsDenyOnly());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
restricted_token =
|
||
|
|
primary_token->CreateRestricted(0, {}, {}, GroupsToSids(groups));
|
||
|
|
ASSERT_TRUE(restricted_token);
|
||
|
|
EXPECT_TRUE(restricted_token->IsRestricted());
|
||
|
|
std::vector<AccessToken::Group> restricted_sids =
|
||
|
|
restricted_token->RestrictedSids();
|
||
|
|
ASSERT_EQ(groups.size(), restricted_sids.size());
|
||
|
|
for (size_t i = 0; i < groups.size(); ++i) {
|
||
|
|
EXPECT_EQ(groups[i].GetSid(), restricted_sids[i].GetSid());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, CreateAppContainer) {
|
||
|
|
absl::optional<AccessToken> primary_token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(primary_token);
|
||
|
|
absl::optional<Sid> package_sid =
|
||
|
|
Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-7");
|
||
|
|
ASSERT_TRUE(package_sid);
|
||
|
|
CheckTokenError(primary_token->CreateAppContainer(*package_sid, {}),
|
||
|
|
ERROR_ACCESS_DENIED);
|
||
|
|
primary_token = AccessToken::FromCurrentProcess(false, TOKEN_ALL_ACCESS);
|
||
|
|
absl::optional<AccessToken> ac_token =
|
||
|
|
primary_token->CreateAppContainer(*package_sid, {});
|
||
|
|
ASSERT_TRUE(ac_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*ac_token), DWORD{TOKEN_QUERY});
|
||
|
|
EXPECT_TRUE(ac_token->IsAppContainer());
|
||
|
|
EXPECT_EQ(ac_token->AppContainerSid(), package_sid);
|
||
|
|
EXPECT_EQ(ac_token->Capabilities().size(), 0U);
|
||
|
|
ac_token =
|
||
|
|
primary_token->CreateAppContainer(*package_sid, {}, kTokenAllNoQuery);
|
||
|
|
ASSERT_TRUE(ac_token);
|
||
|
|
EXPECT_TRUE(ac_token->IsAppContainer());
|
||
|
|
EXPECT_EQ(GetTokenAccess(*ac_token), DWORD{TOKEN_ALL_ACCESS});
|
||
|
|
|
||
|
|
absl::optional<std::vector<Sid>> caps =
|
||
|
|
Sid::FromKnownCapabilityVector({WellKnownCapability::kInternetClient,
|
||
|
|
WellKnownCapability::kDocumentsLibrary});
|
||
|
|
ASSERT_TRUE(caps);
|
||
|
|
ac_token = primary_token->CreateAppContainer(*package_sid, *caps);
|
||
|
|
ASSERT_TRUE(ac_token);
|
||
|
|
EXPECT_EQ(GetTokenAccess(*ac_token), DWORD{TOKEN_QUERY});
|
||
|
|
EXPECT_TRUE(ac_token->IsAppContainer());
|
||
|
|
EXPECT_EQ(ac_token->AppContainerSid(), package_sid);
|
||
|
|
std::vector<AccessToken::Group> cap_groups = ac_token->Capabilities();
|
||
|
|
ASSERT_EQ(cap_groups.size(), caps->size());
|
||
|
|
for (size_t index = 0; index < cap_groups.size(); ++index) {
|
||
|
|
EXPECT_EQ(cap_groups[index].GetSid(), caps->at(index));
|
||
|
|
EXPECT_EQ(cap_groups[index].GetAttributes(), DWORD{SE_GROUP_ENABLED});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, SetPrivilege) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess(true);
|
||
|
|
EXPECT_FALSE(token->SetPrivilege(SE_CHANGE_NOTIFY_NAME, false));
|
||
|
|
token = AccessToken::FromCurrentProcess(true, TOKEN_ADJUST_PRIVILEGES);
|
||
|
|
EXPECT_FALSE(token->SetPrivilege(L"ThisIsNotValid", false));
|
||
|
|
absl::optional<bool> original_state =
|
||
|
|
token->SetPrivilege(SE_CHANGE_NOTIFY_NAME, false);
|
||
|
|
EXPECT_TRUE(original_state);
|
||
|
|
absl::optional<bool> curr_state =
|
||
|
|
token->SetPrivilege(SE_CHANGE_NOTIFY_NAME, true);
|
||
|
|
EXPECT_TRUE(curr_state);
|
||
|
|
EXPECT_FALSE(*curr_state);
|
||
|
|
curr_state = token->SetPrivilege(SE_CHANGE_NOTIFY_NAME, false);
|
||
|
|
EXPECT_TRUE(curr_state);
|
||
|
|
EXPECT_TRUE(*curr_state);
|
||
|
|
EXPECT_TRUE(token->SetPrivilege(SE_CHANGE_NOTIFY_NAME, *original_state));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, RemovePrivilege) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess(true);
|
||
|
|
EXPECT_FALSE(token->RemovePrivilege(SE_CHANGE_NOTIFY_NAME));
|
||
|
|
token = AccessToken::FromCurrentProcess(true, TOKEN_ADJUST_PRIVILEGES);
|
||
|
|
EXPECT_FALSE(token->RemovePrivilege(L"ThisIsNotValid"));
|
||
|
|
EXPECT_TRUE(token->RemovePrivilege(SE_CHANGE_NOTIFY_NAME));
|
||
|
|
EXPECT_FALSE(token->RemovePrivilege(SE_CHANGE_NOTIFY_NAME));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(AccessTokenTest, CheckRelease) {
|
||
|
|
absl::optional<AccessToken> token = AccessToken::FromCurrentProcess();
|
||
|
|
ASSERT_TRUE(token);
|
||
|
|
EXPECT_TRUE(token->is_valid());
|
||
|
|
ScopedHandle handle(token->release());
|
||
|
|
EXPECT_TRUE(handle.is_valid());
|
||
|
|
EXPECT_FALSE(token->is_valid());
|
||
|
|
EXPECT_EQ(token->get(), nullptr);
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace base::win
|