|
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 <string> |
|
6 |
|
7 #include "sandbox/win/src/registry_policy.h" |
|
8 |
|
9 #include "base/logging.h" |
|
10 #include "sandbox/win/src/ipc_tags.h" |
|
11 #include "sandbox/win/src/policy_engine_opcodes.h" |
|
12 #include "sandbox/win/src/policy_params.h" |
|
13 #include "sandbox/win/src/sandbox_utils.h" |
|
14 #include "sandbox/win/src/sandbox_types.h" |
|
15 #include "sandbox/win/src/win_utils.h" |
|
16 |
|
17 namespace { |
|
18 |
|
19 static const DWORD kAllowedRegFlags = KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS | |
|
20 KEY_NOTIFY | KEY_READ | GENERIC_READ | |
|
21 GENERIC_EXECUTE | READ_CONTROL; |
|
22 |
|
23 // Opens the key referenced by |obj_attributes| with |access| and |
|
24 // checks what permission was given. Remove the WRITE flags and update |
|
25 // |access| with the new value. |
|
26 NTSTATUS TranslateMaximumAllowed(OBJECT_ATTRIBUTES* obj_attributes, |
|
27 DWORD* access) { |
|
28 NtOpenKeyFunction NtOpenKey = NULL; |
|
29 ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); |
|
30 |
|
31 NtCloseFunction NtClose = NULL; |
|
32 ResolveNTFunctionPtr("NtClose", &NtClose); |
|
33 |
|
34 NtQueryObjectFunction NtQueryObject = NULL; |
|
35 ResolveNTFunctionPtr("NtQueryObject", &NtQueryObject); |
|
36 |
|
37 // Open the key. |
|
38 HANDLE handle; |
|
39 NTSTATUS status = NtOpenKey(&handle, *access, obj_attributes); |
|
40 if (!NT_SUCCESS(status)) |
|
41 return status; |
|
42 |
|
43 OBJECT_BASIC_INFORMATION info = {0}; |
|
44 status = NtQueryObject(handle, ObjectBasicInformation, &info, sizeof(info), |
|
45 NULL); |
|
46 NtClose(handle); |
|
47 if (!NT_SUCCESS(status)) |
|
48 return status; |
|
49 |
|
50 *access = info.GrantedAccess & kAllowedRegFlags; |
|
51 return STATUS_SUCCESS; |
|
52 } |
|
53 |
|
54 NTSTATUS NtCreateKeyInTarget(HANDLE* target_key_handle, |
|
55 ACCESS_MASK desired_access, |
|
56 OBJECT_ATTRIBUTES* obj_attributes, |
|
57 ULONG title_index, |
|
58 UNICODE_STRING* class_name, |
|
59 ULONG create_options, |
|
60 ULONG* disposition, |
|
61 HANDLE target_process) { |
|
62 NtCreateKeyFunction NtCreateKey = NULL; |
|
63 ResolveNTFunctionPtr("NtCreateKey", &NtCreateKey); |
|
64 |
|
65 if (MAXIMUM_ALLOWED & desired_access) { |
|
66 NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); |
|
67 if (!NT_SUCCESS(status)) |
|
68 return STATUS_ACCESS_DENIED; |
|
69 } |
|
70 |
|
71 HANDLE local_handle = INVALID_HANDLE_VALUE; |
|
72 NTSTATUS status = NtCreateKey(&local_handle, desired_access, obj_attributes, |
|
73 title_index, class_name, create_options, |
|
74 disposition); |
|
75 if (!NT_SUCCESS(status)) |
|
76 return status; |
|
77 |
|
78 if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, |
|
79 target_process, target_key_handle, 0, FALSE, |
|
80 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { |
|
81 return STATUS_ACCESS_DENIED; |
|
82 } |
|
83 return STATUS_SUCCESS; |
|
84 } |
|
85 |
|
86 NTSTATUS NtOpenKeyInTarget(HANDLE* target_key_handle, |
|
87 ACCESS_MASK desired_access, |
|
88 OBJECT_ATTRIBUTES* obj_attributes, |
|
89 HANDLE target_process) { |
|
90 NtOpenKeyFunction NtOpenKey = NULL; |
|
91 ResolveNTFunctionPtr("NtOpenKey", &NtOpenKey); |
|
92 |
|
93 if (MAXIMUM_ALLOWED & desired_access) { |
|
94 NTSTATUS status = TranslateMaximumAllowed(obj_attributes, &desired_access); |
|
95 if (!NT_SUCCESS(status)) |
|
96 return STATUS_ACCESS_DENIED; |
|
97 } |
|
98 |
|
99 HANDLE local_handle = INVALID_HANDLE_VALUE; |
|
100 NTSTATUS status = NtOpenKey(&local_handle, desired_access, obj_attributes); |
|
101 |
|
102 if (!NT_SUCCESS(status)) |
|
103 return status; |
|
104 |
|
105 if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, |
|
106 target_process, target_key_handle, 0, FALSE, |
|
107 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { |
|
108 return STATUS_ACCESS_DENIED; |
|
109 } |
|
110 return STATUS_SUCCESS; |
|
111 } |
|
112 |
|
113 } |
|
114 |
|
115 namespace sandbox { |
|
116 |
|
117 bool RegistryPolicy::GenerateRules(const wchar_t* name, |
|
118 TargetPolicy::Semantics semantics, |
|
119 LowLevelPolicy* policy) { |
|
120 std::wstring resovled_name(name); |
|
121 if (resovled_name.empty()) { |
|
122 return false; |
|
123 } |
|
124 |
|
125 if (!ResolveRegistryName(resovled_name, &resovled_name)) |
|
126 return false; |
|
127 |
|
128 name = resovled_name.c_str(); |
|
129 |
|
130 EvalResult result = ASK_BROKER; |
|
131 |
|
132 PolicyRule open(result); |
|
133 PolicyRule create(result); |
|
134 |
|
135 switch (semantics) { |
|
136 case TargetPolicy::REG_ALLOW_READONLY: { |
|
137 // We consider all flags that are not known to be readonly as potentially |
|
138 // used for write. Here we also support MAXIMUM_ALLOWED, but we are going |
|
139 // to expand it to read-only before the call. |
|
140 DWORD restricted_flags = ~(kAllowedRegFlags | MAXIMUM_ALLOWED); |
|
141 open.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); |
|
142 create.AddNumberMatch(IF_NOT, OpenKey::ACCESS, restricted_flags, AND); |
|
143 break; |
|
144 } |
|
145 case TargetPolicy::REG_ALLOW_ANY: { |
|
146 break; |
|
147 } |
|
148 default: { |
|
149 NOTREACHED(); |
|
150 return false; |
|
151 } |
|
152 } |
|
153 |
|
154 if (!create.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || |
|
155 !policy->AddRule(IPC_NTCREATEKEY_TAG, &create)) { |
|
156 return false; |
|
157 } |
|
158 |
|
159 if (!open.AddStringMatch(IF, OpenKey::NAME, name, CASE_INSENSITIVE) || |
|
160 !policy->AddRule(IPC_NTOPENKEY_TAG, &open)) { |
|
161 return false; |
|
162 } |
|
163 |
|
164 return true; |
|
165 } |
|
166 |
|
167 bool RegistryPolicy::CreateKeyAction(EvalResult eval_result, |
|
168 const ClientInfo& client_info, |
|
169 const std::wstring &key, |
|
170 uint32 attributes, |
|
171 HANDLE root_directory, |
|
172 uint32 desired_access, |
|
173 uint32 title_index, |
|
174 uint32 create_options, |
|
175 HANDLE* handle, |
|
176 NTSTATUS* nt_status, |
|
177 ULONG* disposition) { |
|
178 // The only action supported is ASK_BROKER which means create the requested |
|
179 // file as specified. |
|
180 if (ASK_BROKER != eval_result) { |
|
181 *nt_status = STATUS_ACCESS_DENIED; |
|
182 return false; |
|
183 } |
|
184 |
|
185 // We don't support creating link keys, volatile keys or backup/restore. |
|
186 if (create_options) { |
|
187 *nt_status = STATUS_ACCESS_DENIED; |
|
188 return false; |
|
189 } |
|
190 |
|
191 UNICODE_STRING uni_name = {0}; |
|
192 OBJECT_ATTRIBUTES obj_attributes = {0}; |
|
193 InitObjectAttribs(key, attributes, root_directory, &obj_attributes, |
|
194 &uni_name); |
|
195 *nt_status = NtCreateKeyInTarget(handle, desired_access, &obj_attributes, |
|
196 title_index, NULL, create_options, |
|
197 disposition, client_info.process); |
|
198 return true; |
|
199 } |
|
200 |
|
201 bool RegistryPolicy::OpenKeyAction(EvalResult eval_result, |
|
202 const ClientInfo& client_info, |
|
203 const std::wstring &key, |
|
204 uint32 attributes, |
|
205 HANDLE root_directory, |
|
206 uint32 desired_access, |
|
207 HANDLE* handle, |
|
208 NTSTATUS* nt_status) { |
|
209 // The only action supported is ASK_BROKER which means open the requested |
|
210 // file as specified. |
|
211 if (ASK_BROKER != eval_result) { |
|
212 *nt_status = STATUS_ACCESS_DENIED; |
|
213 return true; |
|
214 } |
|
215 |
|
216 UNICODE_STRING uni_name = {0}; |
|
217 OBJECT_ATTRIBUTES obj_attributes = {0}; |
|
218 InitObjectAttribs(key, attributes, root_directory, &obj_attributes, |
|
219 &uni_name); |
|
220 *nt_status = NtOpenKeyInTarget(handle, desired_access, &obj_attributes, |
|
221 client_info.process); |
|
222 return true; |
|
223 } |
|
224 |
|
225 } // namespace sandbox |