summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc')
-rw-r--r--security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc829
1 files changed, 829 insertions, 0 deletions
diff --git a/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
new file mode 100644
index 0000000000..1855054a7c
--- /dev/null
+++ b/security/sandbox/chromium/sandbox/win/src/restricted_token_unittest.cc
@@ -0,0 +1,829 @@
+// Copyright (c) 2012 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.
+
+// This file contains unit tests for the RestrictedToken.
+
+#include "sandbox/win/src/restricted_token.h"
+
+#include <vector>
+
+#include "base/win/atl.h"
+#include "base/win/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "sandbox/win/src/acl.h"
+#include "sandbox/win/src/security_capabilities.h"
+#include "sandbox/win/src/sid.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace sandbox {
+
+namespace {
+
+void TestDefaultDalc(bool restricted_required) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ if (!restricted_required)
+ token.SetLockdownDefaultDacl();
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(handle.Take());
+
+ ATL::CDacl dacl;
+ ASSERT_TRUE(restricted_token.GetDefaultDacl(&dacl));
+
+ ATL::CSid logon_sid;
+ ASSERT_TRUE(restricted_token.GetLogonSid(&logon_sid));
+
+ bool restricted_found = false;
+ bool logon_sid_found = false;
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ dacl.GetAclEntry(i, &sid, &mask);
+ if (sid == ATL::Sids::RestrictedCode() && mask == GENERIC_ALL) {
+ restricted_found = true;
+ } else if (sid == logon_sid) {
+ logon_sid_found = true;
+ }
+ }
+
+ ASSERT_EQ(restricted_required, restricted_found);
+ if (!restricted_required)
+ ASSERT_FALSE(logon_sid_found);
+}
+
+bool GetVariableTokenInformation(HANDLE token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ DWORD return_length;
+ if (!::GetTokenInformation(token, information_class, nullptr, 0,
+ &return_length)) {
+ if (::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ return false;
+ }
+ }
+
+ information->resize(return_length);
+ return !!::GetTokenInformation(token, information_class, information->data(),
+ return_length, &return_length);
+}
+
+bool GetVariableTokenInformation(const base::win::ScopedHandle& token,
+ TOKEN_INFORMATION_CLASS information_class,
+ std::vector<char>* information) {
+ return GetVariableTokenInformation(token.Get(), information_class,
+ information);
+}
+
+void CheckDaclForPackageSid(const base::win::ScopedHandle& token,
+ PSECURITY_CAPABILITIES security_capabilities,
+ bool package_sid_required) {
+ DWORD length_needed = 0;
+ ::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION, nullptr, 0,
+ &length_needed);
+ ASSERT_EQ(::GetLastError(), DWORD{ERROR_INSUFFICIENT_BUFFER});
+
+ std::vector<char> security_desc_buffer(length_needed);
+ SECURITY_DESCRIPTOR* security_desc =
+ reinterpret_cast<SECURITY_DESCRIPTOR*>(security_desc_buffer.data());
+
+ ASSERT_TRUE(::GetKernelObjectSecurity(token.Get(), DACL_SECURITY_INFORMATION,
+ security_desc, length_needed,
+ &length_needed));
+
+ ATL::CSecurityDesc token_sd(*security_desc);
+ ATL::CDacl dacl;
+ ASSERT_TRUE(token_sd.GetDacl(&dacl));
+
+ ATL::CSid package_sid(
+ static_cast<SID*>(security_capabilities->AppContainerSid));
+ ATL::CSid all_package_sid(
+ static_cast<SID*>(sandbox::Sid(::WinBuiltinAnyPackageSid).GetPSID()));
+
+ unsigned int ace_count = dacl.GetAceCount();
+ for (unsigned int i = 0; i < ace_count; ++i) {
+ ATL::CSid sid;
+ ACCESS_MASK mask = 0;
+ BYTE type = 0;
+ dacl.GetAclEntry(i, &sid, &mask, &type);
+ if (mask != TOKEN_ALL_ACCESS || type != ACCESS_ALLOWED_ACE_TYPE)
+ continue;
+ if (sid == package_sid)
+ EXPECT_TRUE(package_sid_required);
+ else if (sid == all_package_sid)
+ EXPECT_FALSE(package_sid_required);
+ }
+}
+
+void CheckLowBoxToken(const base::win::ScopedHandle& token,
+ TOKEN_TYPE token_type,
+ PSECURITY_CAPABILITIES security_capabilities) {
+ DWORD appcontainer;
+ DWORD return_length;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenIsAppContainer,
+ &appcontainer, sizeof(appcontainer),
+ &return_length));
+ ASSERT_TRUE(appcontainer);
+ TOKEN_TYPE token_type_real;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenType, &token_type_real,
+ sizeof(token_type_real), &return_length));
+ ASSERT_EQ(token_type_real, token_type);
+ if (token_type == ::TokenImpersonation) {
+ SECURITY_IMPERSONATION_LEVEL imp_level;
+ ASSERT_TRUE(::GetTokenInformation(token.Get(), ::TokenImpersonationLevel,
+ &imp_level, sizeof(imp_level),
+ &return_length));
+ ASSERT_EQ(imp_level, ::SecurityImpersonation);
+ }
+
+ std::vector<char> package_sid_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenAppContainerSid,
+ &package_sid_buf));
+ PTOKEN_APPCONTAINER_INFORMATION package_sid =
+ reinterpret_cast<PTOKEN_APPCONTAINER_INFORMATION>(package_sid_buf.data());
+ EXPECT_TRUE(::EqualSid(security_capabilities->AppContainerSid,
+ package_sid->TokenAppContainer));
+
+ std::vector<char> capabilities_buf;
+ ASSERT_TRUE(GetVariableTokenInformation(token, ::TokenCapabilities,
+ &capabilities_buf));
+ PTOKEN_GROUPS capabilities =
+ reinterpret_cast<PTOKEN_GROUPS>(capabilities_buf.data());
+ ASSERT_EQ(capabilities->GroupCount, security_capabilities->CapabilityCount);
+ for (DWORD index = 0; index < capabilities->GroupCount; ++index) {
+ EXPECT_EQ(capabilities->Groups[index].Attributes,
+ security_capabilities->Capabilities[index].Attributes);
+ EXPECT_TRUE(::EqualSid(capabilities->Groups[index].Sid,
+ security_capabilities->Capabilities[index].Sid));
+ }
+
+ CheckDaclForPackageSid(token, security_capabilities, true);
+}
+
+// Checks if a sid is in the restricting list of the restricted token.
+// Asserts if it's not the case. If count is a positive number, the number of
+// elements in the restricting sids list has to be equal.
+void CheckRestrictingSid(HANDLE restricted_token, ATL::CSid sid, int count) {
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token,
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
+ ATL::CTokenGroups atl_groups(*groups);
+
+ if (count >= 0)
+ ASSERT_EQ(static_cast<unsigned>(count), atl_groups.GetCount());
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ atl_groups.GetSidsAndAttributes(&sids, &attributes);
+
+ bool present = false;
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (sids[i] == sid) {
+ present = true;
+ break;
+ }
+ }
+
+ ASSERT_TRUE(present);
+}
+
+void CheckRestrictingSid(const ATL::CAccessToken& restricted_token,
+ ATL::CSid sid,
+ int count) {
+ CheckRestrictingSid(restricted_token.GetHandle(), sid, count);
+}
+
+} // namespace
+
+// Tests the initializatioin with an invalid token handle.
+TEST(RestrictedTokenTest, InvalidHandle) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_INVALID_HANDLE),
+ token.Init(reinterpret_cast<HANDLE>(0x5555)));
+}
+
+// Tests the initialization with nullptr as parameter.
+TEST(RestrictedTokenTest, DefaultInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Create the token using the current token.
+ RestrictedToken token_default;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token_default.Init(nullptr));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token_default.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_user_restricted;
+ ATL::CSid sid_user_default;
+ ATL::CSid sid_owner_restricted;
+ ATL::CSid sid_owner_default;
+ ASSERT_TRUE(restricted_token.GetUser(&sid_user_restricted));
+ ASSERT_TRUE(access_token.GetUser(&sid_user_default));
+ ASSERT_TRUE(restricted_token.GetOwner(&sid_owner_restricted));
+ ASSERT_TRUE(access_token.GetOwner(&sid_owner_default));
+
+ // Check if both token have the same owner and user.
+ ASSERT_EQ(sid_user_restricted, sid_user_default);
+ ASSERT_EQ(sid_owner_restricted, sid_owner_default);
+}
+
+// Tests the initialization with a custom token as parameter.
+TEST(RestrictedTokenTest, CustomInit) {
+ // Get the current process token.
+ HANDLE token_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &token_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, token_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(token_handle);
+
+ // Change the primary group.
+ access_token.SetPrimaryGroup(ATL::Sids::World());
+
+ // Create the token using the current token.
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+
+ // Get the handle to the restricted token.
+
+ base::win::ScopedHandle restricted_token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(restricted_token_handle.Take());
+
+ ATL::CSid sid_restricted;
+ ATL::CSid sid_default;
+ ASSERT_TRUE(restricted_token.GetPrimaryGroup(&sid_restricted));
+ ASSERT_TRUE(access_token.GetPrimaryGroup(&sid_default));
+
+ // Check if both token have the same owner.
+ ASSERT_EQ(sid_restricted, sid_default);
+}
+
+// Verifies that the token created by the object are valid.
+TEST(RestrictedTokenTest, ResultToken) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+
+ base::win::ScopedHandle restricted_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&restricted_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(restricted_token.Get()));
+
+ DWORD length = 0;
+ TOKEN_TYPE type;
+ ASSERT_TRUE(::GetTokenInformation(restricted_token.Get(), ::TokenType, &type,
+ sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenPrimary);
+
+ base::win::ScopedHandle impersonation_token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedTokenForImpersonation(&impersonation_token));
+
+ ASSERT_TRUE(::IsTokenRestricted(impersonation_token.Get()));
+
+ ASSERT_TRUE(::GetTokenInformation(impersonation_token.Get(), ::TokenType,
+ &type, sizeof(type), &length));
+
+ ASSERT_EQ(type, TokenImpersonation);
+}
+
+// Verifies that the token created has "Restricted" in its default dacl.
+TEST(RestrictedTokenTest, DefaultDacl) {
+ TestDefaultDalc(true);
+}
+
+// Verifies that the token created does not have "Restricted" in its default
+// dacl.
+TEST(RestrictedTokenTest, DefaultDaclLockdown) {
+ TestDefaultDalc(false);
+}
+
+// Tests the method "AddSidForDenyOnly".
+TEST(RestrictedTokenTest, DenySid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddSidForDenyOnly(Sid(WinWorldSid)));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly".
+TEST(RestrictedTokenTest, DenySids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method "AddAllSidsForDenyOnly" using an exception list.
+TEST(RestrictedTokenTest, DenySidsException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<Sid> sids_exception;
+ sids_exception.push_back(Sid(WinWorldSid));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddAllSidsForDenyOnly(&sids_exception));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all sids are really gone.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_LOGON_ID) == 0 &&
+ (attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ if (ATL::Sids::World() == sids[i]) {
+ ASSERT_EQ(0u, attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ } else {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly.
+TEST(RestrictedTokenTest, DenyOwnerSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests test method AddOwnerSidForDenyOnly with a custom effective token.
+TEST(RestrictedTokenTest, DenyOwnerSidCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.AddUserSidForDenyOnly());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ ATL::CSid user_sid;
+ ASSERT_TRUE(restricted_token.GetUser(&user_sid));
+
+ for (unsigned int i = 0; i < sids.GetCount(); ++i) {
+ if (user_sid == sids[i]) {
+ ASSERT_EQ(static_cast<DWORD>(SE_GROUP_USE_FOR_DENY_ONLY),
+ attributes[i] & SE_GROUP_USE_FOR_DENY_ONLY);
+ }
+ }
+}
+
+// Tests the method DeleteAllPrivileges.
+TEST(RestrictedTokenTest, DeleteAllPrivileges) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ASSERT_EQ(0u, privileges.GetCount());
+}
+
+// Tests the method DeleteAllPrivileges with an exception list.
+TEST(RestrictedTokenTest, DeleteAllPrivilegesException) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ std::vector<std::wstring> exceptions;
+ exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeleteAllPrivileges(&exceptions));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ ASSERT_EQ(1u, privileges.GetCount());
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_EQ(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method DeletePrivilege.
+TEST(RestrictedTokenTest, DeletePrivilege) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.DeletePrivilege(SE_CHANGE_NOTIFY_NAME));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenPrivileges privileges;
+ ASSERT_TRUE(restricted_token.GetPrivileges(&privileges));
+
+ ATL::CTokenPrivileges::CNames privilege_names;
+ ATL::CTokenPrivileges::CAttributes privilege_name_attributes;
+ privileges.GetNamesAndAttributes(&privilege_names,
+ &privilege_name_attributes);
+
+ for (unsigned int i = 0; i < privileges.GetCount(); ++i) {
+ ASSERT_NE(privilege_names[i], SE_CHANGE_NOTIFY_NAME);
+ }
+}
+
+// Tests the method AddRestrictingSid.
+TEST(RestrictedTokenTest, AddRestrictingSid) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ CheckRestrictingSid(restricted_token, ATL::Sids::World(), 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUser) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidCurrentUser with a custom effective token.
+TEST(RestrictedTokenTest, AddRestrictingSidCurrentUserCustom) {
+ // Get the current process token.
+ HANDLE access_handle = INVALID_HANDLE_VALUE;
+ ASSERT_TRUE(::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
+ &access_handle));
+
+ ASSERT_NE(INVALID_HANDLE_VALUE, access_handle);
+
+ ATL::CAccessToken access_token;
+ access_token.Attach(access_handle);
+
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.Init(access_token.GetHandle()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+
+ CheckRestrictingSid(restricted_token, user, 1);
+}
+
+// Tests the method AddRestrictingSidLogonSession.
+TEST(RestrictedTokenTest, AddRestrictingSidLogonSession) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ CheckRestrictingSid(restricted_token, session, 1);
+}
+
+// Tests adding a lot of restricting sids.
+TEST(RestrictedTokenTest, AddMultipleRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidCurrentUser());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidLogonSession());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+ ATL::CSid session;
+ restricted_token.GetLogonSid(&session);
+
+ std::vector<char> memory;
+ ASSERT_TRUE(GetVariableTokenInformation(restricted_token.GetHandle(),
+ ::TokenRestrictedSids, &memory));
+ PTOKEN_GROUPS groups = reinterpret_cast<PTOKEN_GROUPS>(memory.data());
+ ATL::CTokenGroups atl_groups(*groups);
+ ASSERT_EQ(3u, atl_groups.GetCount());
+}
+
+// Tests the method "AddRestrictingSidAllSids".
+TEST(RestrictedTokenTest, AddAllSidToRestrictingSids) {
+ RestrictedToken token;
+ base::win::ScopedHandle token_handle;
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.AddRestrictingSidAllSids());
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS),
+ token.GetRestrictedToken(&token_handle));
+
+ ATL::CAccessToken restricted_token;
+ restricted_token.Attach(token_handle.Take());
+
+ ATL::CTokenGroups groups;
+ ASSERT_TRUE(restricted_token.GetGroups(&groups));
+
+ ATL::CSid::CSidArray sids;
+ ATL::CAtlArray<DWORD> attributes;
+ groups.GetSidsAndAttributes(&sids, &attributes);
+
+ // Verify that all group sids are in the restricting sid list.
+ for (unsigned int i = 0; i < sids.GetCount(); i++) {
+ if ((attributes[i] & SE_GROUP_INTEGRITY) == 0) {
+ CheckRestrictingSid(restricted_token, sids[i], -1);
+ }
+ }
+
+ // Verify that the user is in the restricting sid list.
+ ATL::CSid user;
+ restricted_token.GetUser(&user);
+ CheckRestrictingSid(restricted_token, user, -1);
+}
+
+// Checks the error code when the object is initialized twice.
+TEST(RestrictedTokenTest, DoubleInit) {
+ RestrictedToken token;
+ ASSERT_EQ(static_cast<DWORD>(ERROR_SUCCESS), token.Init(nullptr));
+
+ ASSERT_EQ(static_cast<DWORD>(ERROR_ALREADY_INITIALIZED), token.Init(nullptr));
+}
+
+TEST(RestrictedTokenTest, LockdownDefaultDaclNoLogonSid) {
+ ATL::CAccessToken anonymous_token;
+ ASSERT_TRUE(::ImpersonateAnonymousToken(::GetCurrentThread()));
+ ASSERT_TRUE(anonymous_token.GetThreadToken(TOKEN_ALL_ACCESS));
+ ::RevertToSelf();
+ ATL::CSid logon_sid;
+ // Verify that the anonymous token doesn't have the logon sid.
+ ASSERT_FALSE(anonymous_token.GetLogonSid(&logon_sid));
+
+ RestrictedToken token;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.Init(anonymous_token.GetHandle()));
+ token.SetLockdownDefaultDacl();
+
+ base::win::ScopedHandle handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, token.GetRestrictedToken(&handle));
+}
+
+TEST(RestrictedTokenTest, LowBoxToken) {
+ if (base::win::GetVersion() < base::win::Version::WIN8)
+ return;
+ base::win::ScopedHandle token;
+
+ Sid package_sid = Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-7");
+ SecurityCapabilities caps_no_capabilities(package_sid);
+
+ ASSERT_EQ(DWORD{ERROR_INVALID_PARAMETER},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_no_capabilities, nullptr,
+ 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_no_capabilities);
+
+ ASSERT_TRUE(ReplacePackageSidInDacl(token.Get(), SE_KERNEL_OBJECT,
+ Sid(caps_no_capabilities.AppContainerSid),
+ TOKEN_ALL_ACCESS));
+ CheckDaclForPackageSid(token, &caps_no_capabilities, false);
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, IMPERSONATION, &caps_no_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenImpersonation, &caps_no_capabilities);
+
+ std::vector<Sid> capabilities;
+ capabilities.push_back(Sid::FromKnownCapability(kInternetClient));
+ capabilities.push_back(Sid::FromKnownCapability(kPrivateNetworkClientServer));
+ SecurityCapabilities caps_with_capabilities(package_sid, capabilities);
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(nullptr, PRIMARY, &caps_with_capabilities,
+ nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+
+ RestrictedToken restricted_token;
+ base::win::ScopedHandle token_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS}, restricted_token.Init(nullptr));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.AddRestrictingSid(ATL::Sids::World().GetPSID()));
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ restricted_token.GetRestrictedToken(&token_handle));
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY,
+ &caps_with_capabilities, nullptr, 0, &token));
+ ASSERT_TRUE(token.IsValid());
+ CheckLowBoxToken(token, ::TokenPrimary, &caps_with_capabilities);
+ CheckRestrictingSid(token.Get(), ATL::Sids::World(), 1);
+
+ SecurityCapabilities caps_for_handles(
+ Sid::FromSddlString(L"S-1-15-2-1-2-3-4-5-6-8"));
+ base::win::ScopedHandle object_handle;
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, true,
+ &object_handle));
+ HANDLE saved_handles[] = {object_handle.Get()};
+
+ ASSERT_EQ(DWORD{ERROR_SUCCESS},
+ CreateLowBoxToken(token_handle.Get(), PRIMARY, &caps_for_handles,
+ saved_handles, 1, &token));
+ ASSERT_TRUE(token.IsValid());
+ object_handle.Close();
+ ASSERT_FALSE(object_handle.IsValid());
+ ASSERT_EQ(DWORD{ERROR_ALREADY_EXISTS},
+ CreateLowBoxObjectDirectory(caps_for_handles.AppContainerSid, false,
+ &object_handle));
+}
+
+} // namespace sandbox