|
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 #include "sandbox/win/src/sandbox_types.h" |
|
6 #include "sandbox/win/src/sandbox_nt_types.h" |
|
7 #include "sandbox/win/src/policy_engine_params.h" |
|
8 #include "sandbox/win/src/policy_engine_opcodes.h" |
|
9 #include "testing/gtest/include/gtest/gtest.h" |
|
10 |
|
11 |
|
12 #define INIT_GLOBAL_RTL(member) \ |
|
13 g_nt.##member = reinterpret_cast<##member##Function>( \ |
|
14 ::GetProcAddress(ntdll, #member)); \ |
|
15 if (NULL == g_nt.##member) \ |
|
16 return false |
|
17 |
|
18 namespace sandbox { |
|
19 |
|
20 SANDBOX_INTERCEPT NtExports g_nt; |
|
21 |
|
22 bool SetupNtdllImports() { |
|
23 HMODULE ntdll = ::GetModuleHandle(kNtdllName); |
|
24 |
|
25 INIT_GLOBAL_RTL(RtlAllocateHeap); |
|
26 INIT_GLOBAL_RTL(RtlAnsiStringToUnicodeString); |
|
27 INIT_GLOBAL_RTL(RtlCompareUnicodeString); |
|
28 INIT_GLOBAL_RTL(RtlCreateHeap); |
|
29 INIT_GLOBAL_RTL(RtlDestroyHeap); |
|
30 INIT_GLOBAL_RTL(RtlFreeHeap); |
|
31 INIT_GLOBAL_RTL(_strnicmp); |
|
32 INIT_GLOBAL_RTL(strlen); |
|
33 INIT_GLOBAL_RTL(wcslen); |
|
34 |
|
35 return true; |
|
36 } |
|
37 |
|
38 TEST(PolicyEngineTest, ParameterSetTest) { |
|
39 void* pv1 = reinterpret_cast<void*>(0x477EAA5); |
|
40 const void* pv2 = reinterpret_cast<void*>(0x987654); |
|
41 ParameterSet pset1 = ParamPickerMake(pv1); |
|
42 ParameterSet pset2 = ParamPickerMake(pv2); |
|
43 |
|
44 // Test that we can store and retrieve a void pointer: |
|
45 const void* result1 =0; |
|
46 unsigned long result2 = 0; |
|
47 EXPECT_TRUE(pset1.Get(&result1)); |
|
48 EXPECT_TRUE(pv1 == result1); |
|
49 EXPECT_FALSE(pset1.Get(&result2)); |
|
50 EXPECT_TRUE(pset2.Get(&result1)); |
|
51 EXPECT_TRUE(pv2 == result1); |
|
52 EXPECT_FALSE(pset2.Get(&result2)); |
|
53 |
|
54 // Test that we can store and retrieve a ulong: |
|
55 unsigned long number = 12747; |
|
56 ParameterSet pset3 = ParamPickerMake(number); |
|
57 EXPECT_FALSE(pset3.Get(&result1)); |
|
58 EXPECT_TRUE(pset3.Get(&result2)); |
|
59 EXPECT_EQ(number, result2); |
|
60 |
|
61 // Test that we can store and retrieve a string: |
|
62 const wchar_t* txt = L"S231L"; |
|
63 ParameterSet pset4 = ParamPickerMake(txt); |
|
64 const wchar_t* result3 = NULL; |
|
65 EXPECT_TRUE(pset4.Get(&result3)); |
|
66 EXPECT_EQ(0, wcscmp(txt, result3)); |
|
67 } |
|
68 |
|
69 TEST(PolicyEngineTest, OpcodeConstraints) { |
|
70 // Test that PolicyOpcode has no virtual functions |
|
71 // because these objects are copied over to other processes |
|
72 // so they cannot have vtables. |
|
73 EXPECT_FALSE(__is_polymorphic(PolicyOpcode)); |
|
74 // Keep developers from adding smarts to the opcodes which should |
|
75 // be pretty much a bag of bytes with a OO interface. |
|
76 EXPECT_TRUE(__has_trivial_destructor(PolicyOpcode)); |
|
77 EXPECT_TRUE(__has_trivial_constructor(PolicyOpcode)); |
|
78 EXPECT_TRUE(__has_trivial_copy(PolicyOpcode)); |
|
79 } |
|
80 |
|
81 TEST(PolicyEngineTest, TrueFalseOpcodes) { |
|
82 void* dummy = NULL; |
|
83 ParameterSet ppb1 = ParamPickerMake(dummy); |
|
84 char memory[1024]; |
|
85 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
86 |
|
87 // This opcode always evaluates to true. |
|
88 PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); |
|
89 EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&ppb1, 1, NULL)); |
|
90 EXPECT_FALSE(op1->IsAction()); |
|
91 |
|
92 // This opcode always evaluates to false. |
|
93 PolicyOpcode* op2 = opcode_maker.MakeOpAlwaysTrue(kPolNone); |
|
94 EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); |
|
95 |
|
96 // Nulls not allowed on the params. |
|
97 EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 0, NULL)); |
|
98 EXPECT_EQ(EVAL_ERROR, op2->Evaluate(NULL, 1, NULL)); |
|
99 |
|
100 // True and False opcodes do not 'require' a number of parameters |
|
101 EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 0, NULL)); |
|
102 EXPECT_EQ(EVAL_TRUE, op2->Evaluate(&ppb1, 1, NULL)); |
|
103 |
|
104 // Test Inverting the logic. Note that inversion is done outside |
|
105 // any particular opcode evaluation so no need to repeat for all |
|
106 // opcodes. |
|
107 PolicyOpcode* op3 = opcode_maker.MakeOpAlwaysFalse(kPolNegateEval); |
|
108 EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&ppb1, 1, NULL)); |
|
109 PolicyOpcode* op4 = opcode_maker.MakeOpAlwaysTrue(kPolNegateEval); |
|
110 EXPECT_EQ(EVAL_FALSE, op4->Evaluate(&ppb1, 1, NULL)); |
|
111 |
|
112 // Test that we clear the match context |
|
113 PolicyOpcode* op5 = opcode_maker.MakeOpAlwaysTrue(kPolClearContext); |
|
114 MatchContext context; |
|
115 context.position = 1; |
|
116 context.options = kPolUseOREval; |
|
117 EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&ppb1, 1, &context)); |
|
118 EXPECT_EQ(0, context.position); |
|
119 MatchContext context2; |
|
120 EXPECT_EQ(context2.options, context.options); |
|
121 } |
|
122 |
|
123 TEST(PolicyEngineTest, OpcodeMakerCase1) { |
|
124 // Testing that the opcode maker does not overrun the |
|
125 // supplied buffer. It should only be able to make 'count' opcodes. |
|
126 void* dummy = NULL; |
|
127 ParameterSet ppb1 = ParamPickerMake(dummy); |
|
128 |
|
129 char memory[256]; |
|
130 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
131 size_t count = sizeof(memory) / sizeof(PolicyOpcode); |
|
132 |
|
133 for (size_t ix =0; ix != count; ++ix) { |
|
134 PolicyOpcode* op = opcode_maker.MakeOpAlwaysFalse(kPolNone); |
|
135 ASSERT_TRUE(NULL != op); |
|
136 EXPECT_EQ(EVAL_FALSE, op->Evaluate(&ppb1, 1, NULL)); |
|
137 } |
|
138 // There should be no room more another opcode: |
|
139 PolicyOpcode* op1 = opcode_maker.MakeOpAlwaysFalse(kPolNone); |
|
140 ASSERT_TRUE(NULL == op1); |
|
141 } |
|
142 |
|
143 TEST(PolicyEngineTest, OpcodeMakerCase2) { |
|
144 SetupNtdllImports(); |
|
145 // Testing that the opcode maker does not overrun the |
|
146 // supplied buffer. It should only be able to make 'count' opcodes. |
|
147 // The difference with the previous test is that this opcodes allocate |
|
148 // the string 'txt2' inside the same buffer. |
|
149 const wchar_t* txt1 = L"1234"; |
|
150 const wchar_t txt2[] = L"123"; |
|
151 |
|
152 ParameterSet ppb1 = ParamPickerMake(txt1); |
|
153 MatchContext mc1; |
|
154 |
|
155 char memory[256]; |
|
156 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
157 size_t count = sizeof(memory) / (sizeof(PolicyOpcode) + sizeof(txt2)); |
|
158 |
|
159 // Test that it does not overrun the buffer. |
|
160 for (size_t ix =0; ix != count; ++ix) { |
|
161 PolicyOpcode* op = opcode_maker.MakeOpWStringMatch(0, txt2, 0, |
|
162 CASE_SENSITIVE, |
|
163 kPolClearContext); |
|
164 ASSERT_TRUE(NULL != op); |
|
165 EXPECT_EQ(EVAL_TRUE, op->Evaluate(&ppb1, 1, &mc1)); |
|
166 } |
|
167 |
|
168 // There should be no room more another opcode: |
|
169 PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, |
|
170 CASE_SENSITIVE, |
|
171 kPolNone); |
|
172 ASSERT_TRUE(NULL == op1); |
|
173 } |
|
174 |
|
175 TEST(PolicyEngineTest, IntegerOpcodes) { |
|
176 const wchar_t* txt = L"abcdef"; |
|
177 unsigned long num1 = 42; |
|
178 unsigned long num2 = 113377; |
|
179 |
|
180 ParameterSet pp_wrong1 = ParamPickerMake(txt); |
|
181 ParameterSet pp_num1 = ParamPickerMake(num1); |
|
182 ParameterSet pp_num2 = ParamPickerMake(num2); |
|
183 |
|
184 char memory[128]; |
|
185 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
186 |
|
187 // Test basic match for unsigned longs 42 == 42 and 42 != 113377. |
|
188 PolicyOpcode* op_m42 = opcode_maker.MakeOpNumberMatch(0, unsigned long(42), |
|
189 kPolNone); |
|
190 EXPECT_EQ(EVAL_TRUE, op_m42->Evaluate(&pp_num1, 1, NULL)); |
|
191 EXPECT_EQ(EVAL_FALSE, op_m42->Evaluate(&pp_num2, 1, NULL)); |
|
192 EXPECT_EQ(EVAL_ERROR, op_m42->Evaluate(&pp_wrong1, 1, NULL)); |
|
193 |
|
194 // Test basic match for void pointers. |
|
195 const void* vp = NULL; |
|
196 ParameterSet pp_num3 = ParamPickerMake(vp); |
|
197 PolicyOpcode* op_vp_null = opcode_maker.MakeOpVoidPtrMatch(0, NULL, |
|
198 kPolNone); |
|
199 EXPECT_EQ(EVAL_TRUE, op_vp_null->Evaluate(&pp_num3, 1, NULL)); |
|
200 EXPECT_EQ(EVAL_FALSE, op_vp_null->Evaluate(&pp_num1, 1, NULL)); |
|
201 EXPECT_EQ(EVAL_ERROR, op_vp_null->Evaluate(&pp_wrong1, 1, NULL)); |
|
202 |
|
203 // Basic range test [41 43] (inclusive). |
|
204 PolicyOpcode* op_range1 = opcode_maker.MakeOpUlongMatchRange(0, 41, 43, |
|
205 kPolNone); |
|
206 EXPECT_EQ(EVAL_TRUE, op_range1->Evaluate(&pp_num1, 1, NULL)); |
|
207 EXPECT_EQ(EVAL_FALSE, op_range1->Evaluate(&pp_num2, 1, NULL)); |
|
208 EXPECT_EQ(EVAL_ERROR, op_range1->Evaluate(&pp_wrong1, 1, NULL)); |
|
209 } |
|
210 |
|
211 TEST(PolicyEngineTest, LogicalOpcodes) { |
|
212 char memory[128]; |
|
213 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
214 |
|
215 unsigned long num1 = 0x10100702; |
|
216 ParameterSet pp_num1 = ParamPickerMake(num1); |
|
217 |
|
218 PolicyOpcode* op_and1 = opcode_maker.MakeOpUlongAndMatch(0, 0x00100000, |
|
219 kPolNone); |
|
220 EXPECT_EQ(EVAL_TRUE, op_and1->Evaluate(&pp_num1, 1, NULL)); |
|
221 PolicyOpcode* op_and2 = opcode_maker.MakeOpUlongAndMatch(0, 0x00000001, |
|
222 kPolNone); |
|
223 EXPECT_EQ(EVAL_FALSE, op_and2->Evaluate(&pp_num1, 1, NULL)); |
|
224 } |
|
225 |
|
226 TEST(PolicyEngineTest, WCharOpcodes1) { |
|
227 SetupNtdllImports(); |
|
228 |
|
229 const wchar_t* txt1 = L"the quick fox jumps over the lazy dog"; |
|
230 const wchar_t txt2[] = L"the quick"; |
|
231 const wchar_t txt3[] = L" fox jumps"; |
|
232 const wchar_t txt4[] = L"the lazy dog"; |
|
233 const wchar_t txt5[] = L"jumps over"; |
|
234 const wchar_t txt6[] = L"g"; |
|
235 |
|
236 ParameterSet pp_tc1 = ParamPickerMake(txt1); |
|
237 char memory[512]; |
|
238 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
239 |
|
240 PolicyOpcode* op1 = opcode_maker.MakeOpWStringMatch(0, txt2, 0, |
|
241 CASE_SENSITIVE, |
|
242 kPolNone); |
|
243 |
|
244 // Simplest substring match from pos 0. It should be a successful match |
|
245 // and the match context should be updated. |
|
246 MatchContext mc1; |
|
247 EXPECT_EQ(EVAL_TRUE, op1->Evaluate(&pp_tc1, 1, &mc1)); |
|
248 EXPECT_TRUE(_countof(txt2) == mc1.position + 1); |
|
249 |
|
250 // Matching again should fail and the context should be unmodified. |
|
251 EXPECT_EQ(EVAL_FALSE, op1->Evaluate(&pp_tc1, 1, &mc1)); |
|
252 EXPECT_TRUE(_countof(txt2) == mc1.position + 1); |
|
253 |
|
254 // Using the same match context we should continue where we left |
|
255 // in the previous successful match, |
|
256 PolicyOpcode* op3 = opcode_maker.MakeOpWStringMatch(0, txt3, 0, |
|
257 CASE_SENSITIVE, |
|
258 kPolNone); |
|
259 EXPECT_EQ(EVAL_TRUE, op3->Evaluate(&pp_tc1, 1, &mc1)); |
|
260 EXPECT_TRUE(_countof(txt3) + _countof(txt2) == mc1.position + 2); |
|
261 |
|
262 // We now keep on matching but now we skip 6 characters which means |
|
263 // we skip the string ' over '. And we zero the match context. This is |
|
264 // the primitive that we use to build '??'. |
|
265 PolicyOpcode* op4 = opcode_maker.MakeOpWStringMatch(0, txt4, 6, |
|
266 CASE_SENSITIVE, |
|
267 kPolClearContext); |
|
268 EXPECT_EQ(EVAL_TRUE, op4->Evaluate(&pp_tc1, 1, &mc1)); |
|
269 EXPECT_EQ(0, mc1.position); |
|
270 |
|
271 // Test that we can properly match the last part of the string |
|
272 PolicyOpcode* op4b = opcode_maker.MakeOpWStringMatch(0, txt4, kSeekToEnd, |
|
273 CASE_SENSITIVE, |
|
274 kPolClearContext); |
|
275 EXPECT_EQ(EVAL_TRUE, op4b->Evaluate(&pp_tc1, 1, &mc1)); |
|
276 EXPECT_EQ(0, mc1.position); |
|
277 |
|
278 // Test matching 'jumps over' over the entire string. This is the |
|
279 // primitive we build '*' from. |
|
280 PolicyOpcode* op5 = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekForward, |
|
281 CASE_SENSITIVE, kPolNone); |
|
282 EXPECT_EQ(EVAL_TRUE, op5->Evaluate(&pp_tc1, 1, &mc1)); |
|
283 EXPECT_EQ(24, mc1.position); |
|
284 |
|
285 // Test that we don't match because it is not at the end of the string |
|
286 PolicyOpcode* op5b = opcode_maker.MakeOpWStringMatch(0, txt5, kSeekToEnd, |
|
287 CASE_SENSITIVE, |
|
288 kPolNone); |
|
289 EXPECT_EQ(EVAL_FALSE, op5b->Evaluate(&pp_tc1, 1, &mc1)); |
|
290 |
|
291 // Test that we function if the string does not fit. In this case we |
|
292 // try to match 'the lazy dog' against 'he lazy dog'. |
|
293 PolicyOpcode* op6 = opcode_maker.MakeOpWStringMatch(0, txt4, 2, |
|
294 CASE_SENSITIVE, kPolNone); |
|
295 EXPECT_EQ(24, mc1.position); |
|
296 |
|
297 // Testing matching against 'g' which should be the last char. |
|
298 MatchContext mc2; |
|
299 PolicyOpcode* op7 = opcode_maker.MakeOpWStringMatch(0, txt6, kSeekForward, |
|
300 CASE_SENSITIVE, kPolNone); |
|
301 EXPECT_EQ(EVAL_TRUE, op7->Evaluate(&pp_tc1, 1, &mc2)); |
|
302 |
|
303 // Trying to match again should fail since we are in the last char. |
|
304 // This also covers a couple of boundary conditions. |
|
305 EXPECT_EQ(EVAL_FALSE, op7->Evaluate(&pp_tc1, 1, &mc2)); |
|
306 } |
|
307 |
|
308 TEST(PolicyEngineTest, WCharOpcodes2) { |
|
309 SetupNtdllImports(); |
|
310 |
|
311 const wchar_t* path1 = L"c:\\documents and settings\\Microsoft\\BLAH.txt"; |
|
312 const wchar_t txt1[] = L"Settings\\microsoft"; |
|
313 ParameterSet pp_tc1 = ParamPickerMake(path1); |
|
314 |
|
315 char memory[256]; |
|
316 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
317 MatchContext mc1; |
|
318 |
|
319 // Testing case-insensitive does not buy us much since it this option |
|
320 // is just passed to the Microsoft API that we use normally, but just for |
|
321 // coverage, here it is: |
|
322 PolicyOpcode* op1s = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, |
|
323 CASE_SENSITIVE, kPolNone); |
|
324 PolicyOpcode* op1i = opcode_maker.MakeOpWStringMatch(0, txt1, kSeekForward, |
|
325 CASE_INSENSITIVE, |
|
326 kPolNone); |
|
327 EXPECT_EQ(EVAL_FALSE, op1s->Evaluate(&pp_tc1, 1, &mc1)); |
|
328 EXPECT_EQ(EVAL_TRUE, op1i->Evaluate(&pp_tc1, 1, &mc1)); |
|
329 EXPECT_EQ(35, mc1.position); |
|
330 } |
|
331 |
|
332 TEST(PolicyEngineTest, ActionOpcodes) { |
|
333 char memory[256]; |
|
334 OpcodeFactory opcode_maker(memory, sizeof(memory)); |
|
335 MatchContext mc1; |
|
336 void* dummy = NULL; |
|
337 ParameterSet ppb1 = ParamPickerMake(dummy); |
|
338 |
|
339 PolicyOpcode* op1 = opcode_maker.MakeOpAction(ASK_BROKER, kPolNone); |
|
340 EXPECT_TRUE(op1->IsAction()); |
|
341 EXPECT_EQ(ASK_BROKER, op1->Evaluate(&ppb1, 1, &mc1)); |
|
342 } |
|
343 |
|
344 } // namespace sandbox |