OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 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 <aclapi.h> | |
6 #include <sddl.h> | |
7 #include <vector> | |
8 | |
9 #include "sandbox/win/src/restricted_token_utils.h" | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/win/scoped_handle.h" | |
13 #include "base/win/windows_version.h" | |
14 #include "sandbox/win/src/job.h" | |
15 #include "sandbox/win/src/restricted_token.h" | |
16 #include "sandbox/win/src/security_level.h" | |
17 #include "sandbox/win/src/sid.h" | |
18 | |
19 namespace sandbox { | |
20 | |
21 DWORD CreateRestrictedToken(TokenLevel security_level, | |
22 IntegrityLevel integrity_level, | |
23 TokenType token_type, | |
24 bool lockdown_default_dacl, | |
25 base::win::ScopedHandle* token) { | |
26 RestrictedToken restricted_token; | |
27 restricted_token.Init(NULL); // Initialized with the current process token | |
28 if (lockdown_default_dacl) | |
29 restricted_token.SetLockdownDefaultDacl(); | |
30 | |
31 std::vector<base::string16> privilege_exceptions; | |
32 std::vector<Sid> sid_exceptions; | |
33 | |
34 bool deny_sids = true; | |
35 bool remove_privileges = true; | |
36 | |
37 switch (security_level) { | |
38 case USER_UNPROTECTED: { | |
39 deny_sids = false; | |
40 remove_privileges = false; | |
41 break; | |
42 } | |
43 case USER_RESTRICTED_SAME_ACCESS: { | |
44 deny_sids = false; | |
45 remove_privileges = false; | |
46 | |
47 unsigned err_code = restricted_token.AddRestrictingSidAllSids(); | |
48 if (ERROR_SUCCESS != err_code) | |
49 return err_code; | |
50 | |
51 break; | |
52 } | |
53 case USER_NON_ADMIN: { | |
54 sid_exceptions.push_back(WinBuiltinUsersSid); | |
55 sid_exceptions.push_back(WinWorldSid); | |
56 sid_exceptions.push_back(WinInteractiveSid); | |
57 sid_exceptions.push_back(WinAuthenticatedUserSid); | |
58 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); | |
59 break; | |
60 } | |
61 case USER_INTERACTIVE: { | |
62 sid_exceptions.push_back(WinBuiltinUsersSid); | |
63 sid_exceptions.push_back(WinWorldSid); | |
64 sid_exceptions.push_back(WinInteractiveSid); | |
65 sid_exceptions.push_back(WinAuthenticatedUserSid); | |
66 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); | |
67 restricted_token.AddRestrictingSid(WinBuiltinUsersSid); | |
68 restricted_token.AddRestrictingSid(WinWorldSid); | |
69 restricted_token.AddRestrictingSid(WinRestrictedCodeSid); | |
70 restricted_token.AddRestrictingSidCurrentUser(); | |
71 restricted_token.AddRestrictingSidLogonSession(); | |
72 break; | |
73 } | |
74 case USER_LIMITED: { | |
75 sid_exceptions.push_back(WinBuiltinUsersSid); | |
76 sid_exceptions.push_back(WinWorldSid); | |
77 sid_exceptions.push_back(WinInteractiveSid); | |
78 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); | |
79 restricted_token.AddRestrictingSid(WinBuiltinUsersSid); | |
80 restricted_token.AddRestrictingSid(WinWorldSid); | |
81 restricted_token.AddRestrictingSid(WinRestrictedCodeSid); | |
82 | |
83 // This token has to be able to create objects in BNO. | |
84 // Unfortunately, on Vista+, it needs the current logon sid | |
85 // in the token to achieve this. You should also set the process to be | |
86 // low integrity level so it can't access object created by other | |
87 // processes. | |
88 restricted_token.AddRestrictingSidLogonSession(); | |
89 break; | |
90 } | |
91 case USER_RESTRICTED: { | |
92 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME); | |
93 restricted_token.AddUserSidForDenyOnly(); | |
94 restricted_token.AddRestrictingSid(WinRestrictedCodeSid); | |
95 break; | |
96 } | |
97 case USER_LOCKDOWN: { | |
98 restricted_token.AddUserSidForDenyOnly(); | |
99 restricted_token.AddRestrictingSid(WinNullSid); | |
100 break; | |
101 } | |
102 default: { | |
103 return ERROR_BAD_ARGUMENTS; | |
104 } | |
105 } | |
106 | |
107 DWORD err_code = ERROR_SUCCESS; | |
108 if (deny_sids) { | |
109 err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions); | |
110 if (ERROR_SUCCESS != err_code) | |
111 return err_code; | |
112 } | |
113 | |
114 if (remove_privileges) { | |
115 err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions); | |
116 if (ERROR_SUCCESS != err_code) | |
117 return err_code; | |
118 } | |
119 | |
120 restricted_token.SetIntegrityLevel(integrity_level); | |
121 | |
122 switch (token_type) { | |
123 case PRIMARY: { | |
124 err_code = restricted_token.GetRestrictedToken(token); | |
125 break; | |
126 } | |
127 case IMPERSONATION: { | |
128 err_code = restricted_token.GetRestrictedTokenForImpersonation(token); | |
129 break; | |
130 } | |
131 default: { | |
132 err_code = ERROR_BAD_ARGUMENTS; | |
133 break; | |
134 } | |
135 } | |
136 | |
137 return err_code; | |
138 } | |
139 | |
140 DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type, | |
141 const wchar_t* ace_access, | |
142 const wchar_t* integrity_level_sid) { | |
143 // Build the SDDL string for the label. | |
144 base::string16 sddl = L"S:("; // SDDL for a SACL. | |
145 sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label". | |
146 sddl += L";;"; // No Ace Flags. | |
147 sddl += ace_access; // Add the ACE access. | |
148 sddl += L";;;"; // No ObjectType and Inherited Object Type. | |
149 sddl += integrity_level_sid; // Trustee Sid. | |
150 sddl += L")"; | |
151 | |
152 DWORD error = ERROR_SUCCESS; | |
153 PSECURITY_DESCRIPTOR sec_desc = NULL; | |
154 | |
155 PACL sacl = NULL; | |
156 BOOL sacl_present = FALSE; | |
157 BOOL sacl_defaulted = FALSE; | |
158 | |
159 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(), | |
160 SDDL_REVISION, | |
161 &sec_desc, NULL)) { | |
162 if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl, | |
163 &sacl_defaulted)) { | |
164 error = ::SetSecurityInfo(handle, type, | |
165 LABEL_SECURITY_INFORMATION, NULL, NULL, NULL, | |
166 sacl); | |
167 } else { | |
168 error = ::GetLastError(); | |
169 } | |
170 | |
171 ::LocalFree(sec_desc); | |
172 } else { | |
173 return::GetLastError(); | |
174 } | |
175 | |
176 return error; | |
177 } | |
178 | |
179 const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) { | |
180 switch (integrity_level) { | |
181 case INTEGRITY_LEVEL_SYSTEM: | |
182 return L"S-1-16-16384"; | |
183 case INTEGRITY_LEVEL_HIGH: | |
184 return L"S-1-16-12288"; | |
185 case INTEGRITY_LEVEL_MEDIUM: | |
186 return L"S-1-16-8192"; | |
187 case INTEGRITY_LEVEL_MEDIUM_LOW: | |
188 return L"S-1-16-6144"; | |
189 case INTEGRITY_LEVEL_LOW: | |
190 return L"S-1-16-4096"; | |
191 case INTEGRITY_LEVEL_BELOW_LOW: | |
192 return L"S-1-16-2048"; | |
193 case INTEGRITY_LEVEL_UNTRUSTED: | |
194 return L"S-1-16-0"; | |
195 case INTEGRITY_LEVEL_LAST: | |
196 return NULL; | |
197 } | |
198 | |
199 NOTREACHED(); | |
200 return NULL; | |
201 } | |
202 DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) { | |
203 | |
204 const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level); | |
205 if (!integrity_level_str) { | |
206 // No mandatory level specified, we don't change it. | |
207 return ERROR_SUCCESS; | |
208 } | |
209 | |
210 PSID integrity_sid = NULL; | |
211 if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid)) | |
212 return ::GetLastError(); | |
213 | |
214 TOKEN_MANDATORY_LABEL label = {}; | |
215 label.Label.Attributes = SE_GROUP_INTEGRITY; | |
216 label.Label.Sid = integrity_sid; | |
217 | |
218 DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid); | |
219 BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label, | |
220 size); | |
221 auto last_error = ::GetLastError(); | |
222 ::LocalFree(integrity_sid); | |
223 | |
224 return result ? ERROR_SUCCESS : last_error; | |
225 } | |
226 | |
227 DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) { | |
228 | |
229 // We don't check for an invalid level here because we'll just let it | |
230 // fail on the SetTokenIntegrityLevel call later on. | |
231 if (integrity_level == INTEGRITY_LEVEL_LAST) { | |
232 // No mandatory level specified, we don't change it. | |
233 return ERROR_SUCCESS; | |
234 } | |
235 | |
236 HANDLE token_handle; | |
237 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT, | |
238 &token_handle)) | |
239 return ::GetLastError(); | |
240 | |
241 base::win::ScopedHandle token(token_handle); | |
242 | |
243 return SetTokenIntegrityLevel(token.Get(), integrity_level); | |
244 } | |
245 | |
246 DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) { | |
247 | |
248 DWORD last_error = 0; | |
249 DWORD length_needed = 0; | |
250 | |
251 ::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, | |
252 NULL, 0, &length_needed); | |
253 | |
254 last_error = ::GetLastError(); | |
255 if (last_error != ERROR_INSUFFICIENT_BUFFER) | |
256 return last_error; | |
257 | |
258 std::vector<char> security_desc_buffer(length_needed); | |
259 PSECURITY_DESCRIPTOR security_desc = | |
260 reinterpret_cast<PSECURITY_DESCRIPTOR>(&security_desc_buffer[0]); | |
261 | |
262 if (!::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, | |
263 security_desc, length_needed, | |
264 &length_needed)) | |
265 return ::GetLastError(); | |
266 | |
267 PACL sacl = NULL; | |
268 BOOL sacl_present = FALSE; | |
269 BOOL sacl_defaulted = FALSE; | |
270 | |
271 if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present, | |
272 &sacl, &sacl_defaulted)) | |
273 return ::GetLastError(); | |
274 | |
275 for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) { | |
276 PSYSTEM_MANDATORY_LABEL_ACE ace; | |
277 | |
278 if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace)) | |
279 && ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) { | |
280 ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP | |
281 | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP; | |
282 break; | |
283 } | |
284 } | |
285 | |
286 if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION, | |
287 security_desc)) | |
288 return ::GetLastError(); | |
289 | |
290 return ERROR_SUCCESS; | |
291 } | |
292 | |
293 DWORD HardenProcessIntegrityLevelPolicy() { | |
294 | |
295 HANDLE token_handle; | |
296 if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER, | |
297 &token_handle)) | |
298 return ::GetLastError(); | |
299 | |
300 base::win::ScopedHandle token(token_handle); | |
301 | |
302 return HardenTokenIntegrityLevelPolicy(token.Get()); | |
303 } | |
304 | |
305 } // namespace sandbox | |
OLD | NEW |