michael@0: // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: michael@0: #include "sandbox/win/src/sandbox_types.h" michael@0: #include "sandbox/win/src/sandbox_nt_types.h" michael@0: #include "sandbox/win/src/policy_engine_params.h" michael@0: #include "sandbox/win/src/policy_engine_opcodes.h" michael@0: #include "testing/gtest/include/gtest/gtest.h" michael@0: michael@0: michael@0: #define INIT_GLOBAL_RTL(member) \ michael@0: g_nt.##member = reinterpret_cast<##member##Function>( \ michael@0: ::GetProcAddress(ntdll, #member)); \ michael@0: if (NULL == g_nt.##member) \ michael@0: return false michael@0: michael@0: namespace sandbox { michael@0: michael@0: SANDBOX_INTERCEPT NtExports g_nt; michael@0: michael@0: bool SetupNtdllImports() { michael@0: HMODULE ntdll = ::GetModuleHandle(kNtdllName); michael@0: michael@0: INIT_GLOBAL_RTL(RtlAllocateHeap); michael@0: INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); michael@0: INIT_GLOBAL_RTL(RtlCompareUnicodeString); michael@0: INIT_GLOBAL_RTL(RtlCreateHeap); michael@0: INIT_GLOBAL_RTL(RtlDestroyHeap); michael@0: INIT_GLOBAL_RTL(RtlFreeHeap); michael@0: INIT_GLOBAL_RTL(_strnicmp); michael@0: INIT_GLOBAL_RTL(strlen); michael@0: INIT_GLOBAL_RTL(wcslen); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, ParameterSetTest) { michael@0: void* pv1 = reinterpret_cast(0x477EAA5); michael@0: const void* pv2 = reinterpret_cast(0x987654); michael@0: ParameterSet pset1 = ParamPickerMake(pv1); michael@0: ParameterSet pset2 = ParamPickerMake(pv2); michael@0: michael@0: // Test that we can store and retrieve a void pointer: michael@0: const void* result1 =0; michael@0: unsigned long result2 = 0; michael@0: EXPECT_TRUE(pset1.Get(&result1)); michael@0: EXPECT_TRUE(pv1 == result1); michael@0: EXPECT_FALSE(pset1.Get(&result2)); michael@0: EXPECT_TRUE(pset2.Get(&result1)); michael@0: EXPECT_TRUE(pv2 == result1); michael@0: EXPECT_FALSE(pset2.Get(&result2)); michael@0: michael@0: // Test that we can store and retrieve a ulong: michael@0: unsigned long number = 12747; michael@0: ParameterSet pset3 = ParamPickerMake(number); michael@0: EXPECT_FALSE(pset3.Get(&result1)); michael@0: EXPECT_TRUE(pset3.Get(&result2)); michael@0: EXPECT_EQ(number, result2); michael@0: michael@0: // Test that we can store and retrieve a string: michael@0: const wchar_t* txt = L"S231L"; michael@0: ParameterSet pset4 = ParamPickerMake(txt); michael@0: const wchar_t* result3 = NULL; michael@0: EXPECT_TRUE(pset4.Get(&result3)); michael@0: EXPECT_EQ(0, wcscmp(txt, result3)); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, OpcodeConstraints) { michael@0: // Test that PolicyOpcode has no virtual functions michael@0: // because these objects are copied over to other processes michael@0: // so they cannot have vtables. michael@0: EXPECT_FALSE(__is_polymorphic(PolicyOpcode)); michael@0: // Keep developers from adding smarts to the opcodes which should michael@0: // be pretty much a bag of bytes with a OO interface. michael@0: EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode)); michael@0: EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode)); michael@0: EXPECT_TRUE(__has_trivial_copy(PolicyOpcode)); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, TrueFalseOpcodes) { michael@0: void* dummy = NULL; michael@0: ParameterSet ppb1 = ParamPickerMake(dummy); michael@0: char memory[1024]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: michael@0: // This opcode always evaluates to true. michael@0: PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); michael@0: EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL)); michael@0: EXPECT_FALSE(op1->IsAction()); michael@0: michael@0: // This opcode always evaluates to false. michael@0: PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); michael@0: michael@0: // Nulls not allowed on the params. michael@0: EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL)); michael@0: EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL)); michael@0: michael@0: // True and False opcodes do not 'require' a number of parameters michael@0: EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL)); michael@0: EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); michael@0: michael@0: // Test Inverting the logic. Note that inversion is done outside michael@0: // any particular opcode evaluation so no need to repeat for all michael@0: // opcodes. michael@0: PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval); michael@0: EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL)); michael@0: PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval); michael@0: EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL)); michael@0: michael@0: // Test that we clear the match context michael@0: PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext); michael@0: MatchContext context; michael@0: context.position = 1; michael@0: context.options = kPolUseOREval; michael@0: EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context)); michael@0: EXPECT_EQ(0, context.position); michael@0: MatchContext context2; michael@0: EXPECT_EQ(context2.options, context.options); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, OpcodeMakerCase1) { michael@0: // Testing that the opcode maker does not overrun the michael@0: // supplied buffer. It should only be able to make 'count' opcodes. michael@0: void* dummy = NULL; michael@0: ParameterSet ppb1 = ParamPickerMake(dummy); michael@0: michael@0: char memory[256]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: size_t count = sizeof(memory) / sizeof(PolicyOpcode); michael@0: michael@0: for (size_t ix =0; ix != count; ++ix) { michael@0: PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone); michael@0: ASSERT_TRUE(NULL != op); michael@0: EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL)); michael@0: } michael@0: // There should be no room more another opcode: michael@0: PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); michael@0: ASSERT_TRUE(NULL == op1); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, OpcodeMakerCase2) { michael@0: SetupNtdllImports(); michael@0: // Testing that the opcode maker does not overrun the michael@0: // supplied buffer. It should only be able to make 'count' opcodes. michael@0: // The difference with the previous test is that this opcodes allocate michael@0: // the string 'txt2' inside the same buffer. michael@0: const wchar_t* txt1 = L"1234"; michael@0: const wchar_t txt2[] = L"123"; michael@0: michael@0: ParameterSet ppb1 = ParamPickerMake(txt1); michael@0: MatchContext mc1; michael@0: michael@0: char memory[256]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2)); michael@0: michael@0: // Test that it does not overrun the buffer. michael@0: for (size_t ix =0; ix != count; ++ix) { michael@0: PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0, michael@0: CASE_SENSITIVE, michael@0: kPolClearContext); michael@0: ASSERT_TRUE(NULL != op); michael@0: EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1)); michael@0: } michael@0: michael@0: // There should be no room more another opcode: michael@0: PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, michael@0: CASE_SENSITIVE, michael@0: kPolNone); michael@0: ASSERT_TRUE(NULL == op1); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, IntegerOpcodes) { michael@0: const wchar_t* txt = L"abcdef"; michael@0: unsigned long num1 = 42; michael@0: unsigned long num2 = 113377; michael@0: michael@0: ParameterSet pp_wrong1 = ParamPickerMake(txt); michael@0: ParameterSet pp_num1 = ParamPickerMake(num1); michael@0: ParameterSet pp_num2 = ParamPickerMake(num2); michael@0: michael@0: char memory[128]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: michael@0: // Test basic match for unsigned longs 42 == 42 and 42 != 113377. michael@0: PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42), michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL)); michael@0: EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL)); michael@0: EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL)); michael@0: michael@0: // Test basic match for void pointers. michael@0: const void* vp = NULL; michael@0: ParameterSet pp_num3 = ParamPickerMake(vp); michael@0: PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL)); michael@0: EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL)); michael@0: EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL)); michael@0: michael@0: // Basic range test [41 43] (inclusive). michael@0: PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL)); michael@0: EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL)); michael@0: EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL)); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, LogicalOpcodes) { michael@0: char memory[128]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: michael@0: unsigned long num1 = 0x10100702; michael@0: ParameterSet pp_num1 = ParamPickerMake(num1); michael@0: michael@0: PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL)); michael@0: PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL)); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, WCharOpcodes1) { michael@0: SetupNtdllImports(); michael@0: michael@0: const wchar_t* txt1 = L"the quick fox jumps over the lazy dog"; michael@0: const wchar_t txt2[] = L"the quick"; michael@0: const wchar_t txt3[] = L" fox jumps"; michael@0: const wchar_t txt4[] = L"the lazy dog"; michael@0: const wchar_t txt5[] = L"jumps over"; michael@0: const wchar_t txt6[] = L"g"; michael@0: michael@0: ParameterSet pp_tc1 = ParamPickerMake(txt1); michael@0: char memory[512]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: michael@0: PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, michael@0: CASE_SENSITIVE, michael@0: kPolNone); michael@0: michael@0: // Simplest substring match from pos 0. It should be a successful match michael@0: // and the match context should be updated. michael@0: MatchContext mc1; michael@0: EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_TRUE(_countof(txt2) == mc1.position + 1); michael@0: michael@0: // Matching again should fail and the context should be unmodified. michael@0: EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_TRUE(_countof(txt2) == mc1.position + 1); michael@0: michael@0: // Using the same match context we should continue where we left michael@0: // in the previous successful match, michael@0: PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0, michael@0: CASE_SENSITIVE, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2); michael@0: michael@0: // We now keep on matching but now we skip 6 characters which means michael@0: // we skip the string ' over '. And we zero the match context. This is michael@0: // the primitive that we use to build '??'. michael@0: PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6, michael@0: CASE_SENSITIVE, michael@0: kPolClearContext); michael@0: EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_EQ(0, mc1.position); michael@0: michael@0: // Test that we can properly match the last part of the string michael@0: PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd, michael@0: CASE_SENSITIVE, michael@0: kPolClearContext); michael@0: EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_EQ(0, mc1.position); michael@0: michael@0: // Test matching 'jumps over' over the entire string. This is the michael@0: // primitive we build '*' from. michael@0: PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward, michael@0: CASE_SENSITIVE, kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_EQ(24, mc1.position); michael@0: michael@0: // Test that we don't match because it is not at the end of the string michael@0: PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd, michael@0: CASE_SENSITIVE, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1)); michael@0: michael@0: // Test that we function if the string does not fit. In this case we michael@0: // try to match 'the lazy dog' against 'he lazy dog'. michael@0: PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2, michael@0: CASE_SENSITIVE, kPolNone); michael@0: EXPECT_EQ(24, mc1.position); michael@0: michael@0: // Testing matching against 'g' which should be the last char. michael@0: MatchContext mc2; michael@0: PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward, michael@0: CASE_SENSITIVE, kPolNone); michael@0: EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2)); michael@0: michael@0: // Trying to match again should fail since we are in the last char. michael@0: // This also covers a couple of boundary conditions. michael@0: EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2)); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, WCharOpcodes2) { michael@0: SetupNtdllImports(); michael@0: michael@0: const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt"; michael@0: const wchar_t txt1[] = L"Settings\\microsoft"; michael@0: ParameterSet pp_tc1 = ParamPickerMake(path1); michael@0: michael@0: char memory[256]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: MatchContext mc1; michael@0: michael@0: // Testing case-insensitive does not buy us much since it this option michael@0: // is just passed to the Microsoft API that we use normally, but just for michael@0: // coverage, here it is: michael@0: PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, michael@0: CASE_SENSITIVE, kPolNone); michael@0: PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, michael@0: CASE_INSENSITIVE, michael@0: kPolNone); michael@0: EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1)); michael@0: EXPECT_EQ(35, mc1.position); michael@0: } michael@0: michael@0: TEST(PolicyEngineTest, ActionOpcodes) { michael@0: char memory[256]; michael@0: OpcodeFactory opcode_maker(memory, sizeof(memory)); michael@0: MatchContext mc1; michael@0: void* dummy = NULL; michael@0: ParameterSet ppb1 = ParamPickerMake(dummy); michael@0: michael@0: PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); michael@0: EXPECT_TRUE(op1->IsAction()); michael@0: EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1)); michael@0: } michael@0: michael@0: } // namespace sandbox