Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sandbox / win / src / restricted_token_utils.cc
blob4a3d05c639bcd5acbfff79ce2a96df946fc56ce7
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.
5 #include <aclapi.h>
6 #include <sddl.h>
7 #include <vector>
9 #include "sandbox/win/src/restricted_token_utils.h"
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"
19 namespace sandbox {
21 DWORD CreateRestrictedToken(TokenLevel security_level,
22 IntegrityLevel integrity_level,
23 TokenType token_type,
24 base::win::ScopedHandle* token) {
25 RestrictedToken restricted_token;
26 restricted_token.Init(NULL); // Initialized with the current process token
28 std::vector<base::string16> privilege_exceptions;
29 std::vector<Sid> sid_exceptions;
31 bool deny_sids = true;
32 bool remove_privileges = true;
34 switch (security_level) {
35 case USER_UNPROTECTED: {
36 deny_sids = false;
37 remove_privileges = false;
38 break;
40 case USER_RESTRICTED_SAME_ACCESS: {
41 deny_sids = false;
42 remove_privileges = false;
44 unsigned err_code = restricted_token.AddRestrictingSidAllSids();
45 if (ERROR_SUCCESS != err_code)
46 return err_code;
48 break;
50 case USER_NON_ADMIN: {
51 sid_exceptions.push_back(WinBuiltinUsersSid);
52 sid_exceptions.push_back(WinWorldSid);
53 sid_exceptions.push_back(WinInteractiveSid);
54 sid_exceptions.push_back(WinAuthenticatedUserSid);
55 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
56 break;
58 case USER_INTERACTIVE: {
59 sid_exceptions.push_back(WinBuiltinUsersSid);
60 sid_exceptions.push_back(WinWorldSid);
61 sid_exceptions.push_back(WinInteractiveSid);
62 sid_exceptions.push_back(WinAuthenticatedUserSid);
63 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
64 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
65 restricted_token.AddRestrictingSid(WinWorldSid);
66 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
67 restricted_token.AddRestrictingSidCurrentUser();
68 restricted_token.AddRestrictingSidLogonSession();
69 break;
71 case USER_LIMITED: {
72 sid_exceptions.push_back(WinBuiltinUsersSid);
73 sid_exceptions.push_back(WinWorldSid);
74 sid_exceptions.push_back(WinInteractiveSid);
75 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
76 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
77 restricted_token.AddRestrictingSid(WinWorldSid);
78 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
80 // This token has to be able to create objects in BNO.
81 // Unfortunately, on vista, it needs the current logon sid
82 // in the token to achieve this. You should also set the process to be
83 // low integrity level so it can't access object created by other
84 // processes.
85 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
86 restricted_token.AddRestrictingSidLogonSession();
87 break;
89 case USER_RESTRICTED: {
90 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
91 restricted_token.AddUserSidForDenyOnly();
92 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
93 break;
95 case USER_LOCKDOWN: {
96 restricted_token.AddUserSidForDenyOnly();
97 restricted_token.AddRestrictingSid(WinNullSid);
98 break;
100 default: {
101 return ERROR_BAD_ARGUMENTS;
105 DWORD err_code = ERROR_SUCCESS;
106 if (deny_sids) {
107 err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
108 if (ERROR_SUCCESS != err_code)
109 return err_code;
112 if (remove_privileges) {
113 err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
114 if (ERROR_SUCCESS != err_code)
115 return err_code;
118 restricted_token.SetIntegrityLevel(integrity_level);
120 switch (token_type) {
121 case PRIMARY: {
122 err_code = restricted_token.GetRestrictedToken(token);
123 break;
125 case IMPERSONATION: {
126 err_code = restricted_token.GetRestrictedTokenForImpersonation(token);
127 break;
129 default: {
130 err_code = ERROR_BAD_ARGUMENTS;
131 break;
135 return err_code;
138 DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type,
139 const wchar_t* ace_access,
140 const wchar_t* integrity_level_sid) {
141 // Build the SDDL string for the label.
142 base::string16 sddl = L"S:("; // SDDL for a SACL.
143 sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
144 sddl += L";;"; // No Ace Flags.
145 sddl += ace_access; // Add the ACE access.
146 sddl += L";;;"; // No ObjectType and Inherited Object Type.
147 sddl += integrity_level_sid; // Trustee Sid.
148 sddl += L")";
150 DWORD error = ERROR_SUCCESS;
151 PSECURITY_DESCRIPTOR sec_desc = NULL;
153 PACL sacl = NULL;
154 BOOL sacl_present = FALSE;
155 BOOL sacl_defaulted = FALSE;
157 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
158 SDDL_REVISION,
159 &sec_desc, NULL)) {
160 if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
161 &sacl_defaulted)) {
162 error = ::SetSecurityInfo(handle, type,
163 LABEL_SECURITY_INFORMATION, NULL, NULL, NULL,
164 sacl);
165 } else {
166 error = ::GetLastError();
169 ::LocalFree(sec_desc);
170 } else {
171 return::GetLastError();
174 return error;
177 const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
178 switch (integrity_level) {
179 case INTEGRITY_LEVEL_SYSTEM:
180 return L"S-1-16-16384";
181 case INTEGRITY_LEVEL_HIGH:
182 return L"S-1-16-12288";
183 case INTEGRITY_LEVEL_MEDIUM:
184 return L"S-1-16-8192";
185 case INTEGRITY_LEVEL_MEDIUM_LOW:
186 return L"S-1-16-6144";
187 case INTEGRITY_LEVEL_LOW:
188 return L"S-1-16-4096";
189 case INTEGRITY_LEVEL_BELOW_LOW:
190 return L"S-1-16-2048";
191 case INTEGRITY_LEVEL_UNTRUSTED:
192 return L"S-1-16-0";
193 case INTEGRITY_LEVEL_LAST:
194 return NULL;
197 NOTREACHED();
198 return NULL;
200 DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
201 if (base::win::GetVersion() < base::win::VERSION_VISTA)
202 return ERROR_SUCCESS;
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;
210 PSID integrity_sid = NULL;
211 if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
212 return ::GetLastError();
214 TOKEN_MANDATORY_LABEL label = {};
215 label.Label.Attributes = SE_GROUP_INTEGRITY;
216 label.Label.Sid = integrity_sid;
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);
224 return result ? ERROR_SUCCESS : last_error;
227 DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
228 if (base::win::GetVersion() < base::win::VERSION_VISTA)
229 return ERROR_SUCCESS;
231 // We don't check for an invalid level here because we'll just let it
232 // fail on the SetTokenIntegrityLevel call later on.
233 if (integrity_level == INTEGRITY_LEVEL_LAST) {
234 // No mandatory level specified, we don't change it.
235 return ERROR_SUCCESS;
238 HANDLE token_handle;
239 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
240 &token_handle))
241 return ::GetLastError();
243 base::win::ScopedHandle token(token_handle);
245 return SetTokenIntegrityLevel(token.Get(), integrity_level);
248 DWORD HardenTokenIntegrityLevelPolicy(HANDLE token) {
249 if (base::win::GetVersion() < base::win::VERSION_WIN7)
250 return ERROR_SUCCESS;
252 DWORD last_error = 0;
253 DWORD length_needed = 0;
255 ::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
256 NULL, 0, &length_needed);
258 last_error = ::GetLastError();
259 if (last_error != ERROR_INSUFFICIENT_BUFFER)
260 return last_error;
262 std::vector<char> security_desc_buffer(length_needed);
263 PSECURITY_DESCRIPTOR security_desc =
264 reinterpret_cast<PSECURITY_DESCRIPTOR>(&security_desc_buffer[0]);
266 if (!::GetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
267 security_desc, length_needed,
268 &length_needed))
269 return ::GetLastError();
271 PACL sacl = NULL;
272 BOOL sacl_present = FALSE;
273 BOOL sacl_defaulted = FALSE;
275 if (!::GetSecurityDescriptorSacl(security_desc, &sacl_present,
276 &sacl, &sacl_defaulted))
277 return ::GetLastError();
279 for (DWORD ace_index = 0; ace_index < sacl->AceCount; ++ace_index) {
280 PSYSTEM_MANDATORY_LABEL_ACE ace;
282 if (::GetAce(sacl, ace_index, reinterpret_cast<LPVOID*>(&ace))
283 && ace->Header.AceType == SYSTEM_MANDATORY_LABEL_ACE_TYPE) {
284 ace->Mask |= SYSTEM_MANDATORY_LABEL_NO_READ_UP
285 | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP;
286 break;
290 if (!::SetKernelObjectSecurity(token, LABEL_SECURITY_INFORMATION,
291 security_desc))
292 return ::GetLastError();
294 return ERROR_SUCCESS;
297 DWORD HardenProcessIntegrityLevelPolicy() {
298 if (base::win::GetVersion() < base::win::VERSION_WIN7)
299 return ERROR_SUCCESS;
301 HANDLE token_handle;
302 if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL | WRITE_OWNER,
303 &token_handle))
304 return ::GetLastError();
306 base::win::ScopedHandle token(token_handle);
308 return HardenTokenIntegrityLevelPolicy(token.Get());
311 } // namespace sandbox