summaryrefslogtreecommitdiffstats
path: root/sandbox/src/policy_opcodes_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sandbox/src/policy_opcodes_unittest.cc')
-rw-r--r--sandbox/src/policy_opcodes_unittest.cc369
1 files changed, 369 insertions, 0 deletions
diff --git a/sandbox/src/policy_opcodes_unittest.cc b/sandbox/src/policy_opcodes_unittest.cc
new file mode 100644
index 0000000..02b8c6c
--- /dev/null
+++ b/sandbox/src/policy_opcodes_unittest.cc
@@ -0,0 +1,369 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "sandbox/src/sandbox_types.h"
+#include "sandbox/src/sandbox_nt_types.h"
+#include "sandbox/src/policy_engine_params.h"
+#include "sandbox/src/policy_engine_opcodes.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+
+#define INIT_GLOBAL_RTL(member) \
+ g_nt.##member = reinterpret_cast<##member##Function>( \
+ ::GetProcAddress(ntdll, #member)); \
+ if (NULL == g_nt.##member) \
+ return false
+
+namespace sandbox {
+
+SANDBOX_INTERCEPT NtExports g_nt;
+
+bool SetupNtdllImports() {
+ HMODULE ntdll = ::GetModuleHandle(kNtdllName);
+
+ INIT_GLOBAL_RTL(RtlAllocateHeap);
+ INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString);
+ INIT_GLOBAL_RTL(RtlCompareUnicodeString);
+ INIT_GLOBAL_RTL(RtlCreateHeap);
+ INIT_GLOBAL_RTL(RtlDestroyHeap);
+ INIT_GLOBAL_RTL(RtlFreeHeap);
+ INIT_GLOBAL_RTL(_strnicmp);
+ INIT_GLOBAL_RTL(strlen);
+ INIT_GLOBAL_RTL(wcslen);
+
+ return true;
+}
+
+TEST(PolicyEngineTest, ParameterSetTest) {
+ void* pv1 = reinterpret_cast<void*>(0x477EAA5);
+ const void* pv2 = reinterpret_cast<void*>(0x987654);
+ ParameterSet pset1 = ParamPickerMake(pv1);
+ ParameterSet pset2 = ParamPickerMake(pv2);
+
+ // Test that we can store and retrieve a void pointer:
+ const void* result1 =0;
+ unsigned long result2 = 0;
+ EXPECT_TRUE(pset1.Get(&result1));
+ EXPECT_TRUE(pv1 == result1);
+ EXPECT_FALSE(pset1.Get(&result2));
+ EXPECT_TRUE(pset2.Get(&result1));
+ EXPECT_TRUE(pv2 == result1);
+ EXPECT_FALSE(pset2.Get(&result2));
+
+ // Test that we can store and retrieve a ulong:
+ unsigned long number = 12747;
+ ParameterSet pset3 = ParamPickerMake(number);
+ EXPECT_FALSE(pset3.Get(&result1));
+ EXPECT_TRUE(pset3.Get(&result2));
+ EXPECT_EQ(number, result2);
+
+ // Test that we can store and retrieve a string:
+ const wchar_t* txt = L"S231L";
+ ParameterSet pset4 = ParamPickerMake(txt);
+ const wchar_t* result3 = NULL;
+ EXPECT_TRUE(pset4.Get(&result3));
+ EXPECT_EQ(0, wcscmp(txt, result3));
+}
+
+TEST(PolicyEngineTest, OpcodeConstraints) {
+ // Test that PolicyOpcode has no virtual functions
+ // because these objects are copied over to other processes
+ // so they cannot have vtables.
+ EXPECT_FALSE(__is_polymorphic(PolicyOpcode));
+ // Keep developers from adding smarts to the opcodes which should
+ // be pretty much a bag of bytes with a OO interface.
+ EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode));
+ EXPECT_TRUE(__has_trivial_copy(PolicyOpcode));
+}
+
+TEST(PolicyEngineTest, TrueFalseOpcodes) {
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+ char memory[1024];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // This opcode always evaluates to true.
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL));
+ EXPECT_FALSE(op1->IsAction());
+
+ // This opcode always evaluates to false.
+ PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL));
+
+ // Nulls not allowed on the params.
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL));
+ EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL));
+
+ // True and False opcodes do not 'require' a number of parameters
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL));
+ EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL));
+
+ // Test Inverting the logic. Note that inversion is done outside
+ // any particular opcode evaluation so no need to repeat for all
+ // opcodes.
+ PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL));
+ PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval);
+ EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL));
+
+ // Test that we clear the match context
+ PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext);
+ MatchContext context;
+ context.position = 1;
+ context.options = kPolUseOREval;
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context));
+ EXPECT_EQ(0, context.position);
+ MatchContext context2;
+ EXPECT_EQ(context2.options, context.options);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase1) {
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / sizeof(PolicyOpcode);
+
+ for (size_t ix =0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_TRUE(NULL != op);
+ EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL));
+ }
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone);
+ ASSERT_TRUE(NULL == op1);
+}
+
+TEST(PolicyEngineTest, OpcodeMakerCase2) {
+ SetupNtdllImports();
+ // Testing that the opcode maker does not overrun the
+ // supplied buffer. It should only be able to make 'count' opcodes.
+ // The difference with the previous test is that this opcodes allocate
+ // the string 'txt2' inside the same buffer.
+ const wchar_t* txt1 = L"1234";
+ const wchar_t txt2[] = L"123";
+
+ ParameterSet ppb1 = ParamPickerMake(txt1);
+ MatchContext mc1;
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2));
+
+ // Test that it does not overrun the buffer.
+ for (size_t ix =0; ix != count; ++ix) {
+ PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ ASSERT_TRUE(NULL != op);
+ EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1));
+ }
+
+ // There should be no room more another opcode:
+ PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+ ASSERT_TRUE(NULL == op1);
+}
+
+TEST(PolicyEngineTest, IntegerOpcodes) {
+ const wchar_t* txt = L"abcdef";
+ unsigned long num1 = 42;
+ unsigned long num2 = 113377;
+
+ ParameterSet pp_wrong1 = ParamPickerMake(txt);
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+ ParameterSet pp_num2 = ParamPickerMake(num2);
+
+ char memory[128];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ // Test basic match for unsigned longs 42 == 42 and 42 != 113377.
+ PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42),
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL));
+ EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL));
+ EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL));
+
+ // Test basic match for void pointers.
+ const void* vp = NULL;
+ ParameterSet pp_num3 = ParamPickerMake(vp);
+ PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL));
+ EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL));
+ EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL));
+
+ // Basic range test [41 43] (inclusive).
+ PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL));
+ EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL));
+ EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL));
+}
+
+TEST(PolicyEngineTest, LogicalOpcodes) {
+ char memory[128];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ unsigned long num1 = 0x10100702;
+ ParameterSet pp_num1 = ParamPickerMake(num1);
+
+ PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL));
+ PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes1) {
+ SetupNtdllImports();
+
+ const wchar_t* txt1 = L"the quick fox jumps over the lazy dog";
+ const wchar_t txt2[] = L"the quick";
+ const wchar_t txt3[] = L" fox jumps";
+ const wchar_t txt4[] = L"the lazy dog";
+ const wchar_t txt5[] = L"jumps over";
+ const wchar_t txt6[] = L"g";
+
+ ParameterSet pp_tc1 = ParamPickerMake(txt1);
+ char memory[512];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+
+ // Simplest substring match from pos 0. It should be a successful match
+ // and the match context should be updated.
+ MatchContext mc1;
+ EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Matching again should fail and the context should be unmodified.
+ EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt2) == mc1.position + 1);
+
+ // Using the same match context we should continue where we left
+ // in the previous successful match,
+ PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0,
+ CASE_SENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2);
+
+ // We now keep on matching but now we skip 6 characters which means
+ // we skip the string ' over '. And we zero the match context. This is
+ // the primitive that we use to build '??'.
+ PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0, mc1.position);
+
+ // Test that we can properly match the last part of the string
+ PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd,
+ CASE_SENSITIVE,
+ kPolClearContext);
+ EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(0, mc1.position);
+
+ // Test matching 'jumps over' over the entire string. This is the
+ // primitive we build '*' from.
+ PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(24, mc1.position);
+
+ // Test that we don't match because it is not at the end of the string
+ PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd,
+ CASE_SENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1));
+
+ // Test that we function if the string does not fit. In this case we
+ // try to match 'the lazy dog' against 'he lazy dog'.
+ PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(24, mc1.position);
+
+ // Testing matching against 'g' which should be the last char.
+ MatchContext mc2;
+ PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2));
+
+ // Trying to match again should fail since we are in the last char.
+ // This also covers a couple of boundary conditions.
+ EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2));
+}
+
+TEST(PolicyEngineTest, WCharOpcodes2) {
+ SetupNtdllImports();
+
+ const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt";
+ const wchar_t txt1[] = L"Settings\\microsoft";
+ ParameterSet pp_tc1 = ParamPickerMake(path1);
+
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+
+ // Testing case-insensitive does not buy us much since it this option
+ // is just passed to the Microsoft API that we use normally, but just for
+ // coverage, here it is:
+ PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward,
+ CASE_SENSITIVE, kPolNone);
+ PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward,
+ CASE_INSENSITIVE,
+ kPolNone);
+ EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1));
+ EXPECT_EQ(35, mc1.position);
+}
+
+TEST(PolicyEngineTest, ActionOpcodes) {
+ char memory[256];
+ OpcodeFactory opcode_maker(memory, sizeof(memory));
+ MatchContext mc1;
+ void* dummy = NULL;
+ ParameterSet ppb1 = ParamPickerMake(dummy);
+
+ PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone);
+ EXPECT_TRUE(op1->IsAction());
+ EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1));
+}
+
+} // namespace sandbox