security/sandbox/win/src/restricted_token_utils.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include <aclapi.h>
michael@0 6 #include <sddl.h>
michael@0 7 #include <vector>
michael@0 8
michael@0 9 #include "sandbox/win/src/restricted_token_utils.h"
michael@0 10
michael@0 11 #include "base/logging.h"
michael@0 12 #include "base/win/scoped_handle.h"
michael@0 13 #include "base/win/scoped_process_information.h"
michael@0 14 #include "base/win/windows_version.h"
michael@0 15 #include "sandbox/win/src/job.h"
michael@0 16 #include "sandbox/win/src/restricted_token.h"
michael@0 17 #include "sandbox/win/src/security_level.h"
michael@0 18 #include "sandbox/win/src/sid.h"
michael@0 19
michael@0 20 namespace sandbox {
michael@0 21
michael@0 22 DWORD CreateRestrictedToken(HANDLE *token_handle,
michael@0 23 TokenLevel security_level,
michael@0 24 IntegrityLevel integrity_level,
michael@0 25 TokenType token_type) {
michael@0 26 if (!token_handle)
michael@0 27 return ERROR_BAD_ARGUMENTS;
michael@0 28
michael@0 29 RestrictedToken restricted_token;
michael@0 30 restricted_token.Init(NULL); // Initialized with the current process token
michael@0 31
michael@0 32 std::vector<std::wstring> privilege_exceptions;
michael@0 33 std::vector<Sid> sid_exceptions;
michael@0 34
michael@0 35 bool deny_sids = true;
michael@0 36 bool remove_privileges = true;
michael@0 37
michael@0 38 switch (security_level) {
michael@0 39 case USER_UNPROTECTED: {
michael@0 40 deny_sids = false;
michael@0 41 remove_privileges = false;
michael@0 42 break;
michael@0 43 }
michael@0 44 case USER_RESTRICTED_SAME_ACCESS: {
michael@0 45 deny_sids = false;
michael@0 46 remove_privileges = false;
michael@0 47
michael@0 48 unsigned err_code = restricted_token.AddRestrictingSidAllSids();
michael@0 49 if (ERROR_SUCCESS != err_code)
michael@0 50 return err_code;
michael@0 51
michael@0 52 break;
michael@0 53 }
michael@0 54 case USER_NON_ADMIN: {
michael@0 55 sid_exceptions.push_back(WinBuiltinUsersSid);
michael@0 56 sid_exceptions.push_back(WinWorldSid);
michael@0 57 sid_exceptions.push_back(WinInteractiveSid);
michael@0 58 sid_exceptions.push_back(WinAuthenticatedUserSid);
michael@0 59 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
michael@0 60 break;
michael@0 61 }
michael@0 62 case USER_INTERACTIVE: {
michael@0 63 sid_exceptions.push_back(WinBuiltinUsersSid);
michael@0 64 sid_exceptions.push_back(WinWorldSid);
michael@0 65 sid_exceptions.push_back(WinInteractiveSid);
michael@0 66 sid_exceptions.push_back(WinAuthenticatedUserSid);
michael@0 67 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
michael@0 68 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
michael@0 69 restricted_token.AddRestrictingSid(WinWorldSid);
michael@0 70 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
michael@0 71 restricted_token.AddRestrictingSidCurrentUser();
michael@0 72 restricted_token.AddRestrictingSidLogonSession();
michael@0 73 break;
michael@0 74 }
michael@0 75 case USER_LIMITED: {
michael@0 76 sid_exceptions.push_back(WinBuiltinUsersSid);
michael@0 77 sid_exceptions.push_back(WinWorldSid);
michael@0 78 sid_exceptions.push_back(WinInteractiveSid);
michael@0 79 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
michael@0 80 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
michael@0 81 restricted_token.AddRestrictingSid(WinWorldSid);
michael@0 82 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
michael@0 83
michael@0 84 // This token has to be able to create objects in BNO.
michael@0 85 // Unfortunately, on vista, it needs the current logon sid
michael@0 86 // in the token to achieve this. You should also set the process to be
michael@0 87 // low integrity level so it can't access object created by other
michael@0 88 // processes.
michael@0 89 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
michael@0 90 restricted_token.AddRestrictingSidLogonSession();
michael@0 91 break;
michael@0 92 }
michael@0 93 case USER_RESTRICTED: {
michael@0 94 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
michael@0 95 restricted_token.AddUserSidForDenyOnly();
michael@0 96 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
michael@0 97 break;
michael@0 98 }
michael@0 99 case USER_LOCKDOWN: {
michael@0 100 restricted_token.AddUserSidForDenyOnly();
michael@0 101 restricted_token.AddRestrictingSid(WinNullSid);
michael@0 102 break;
michael@0 103 }
michael@0 104 default: {
michael@0 105 return ERROR_BAD_ARGUMENTS;
michael@0 106 }
michael@0 107 }
michael@0 108
michael@0 109 DWORD err_code = ERROR_SUCCESS;
michael@0 110 if (deny_sids) {
michael@0 111 err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
michael@0 112 if (ERROR_SUCCESS != err_code)
michael@0 113 return err_code;
michael@0 114 }
michael@0 115
michael@0 116 if (remove_privileges) {
michael@0 117 err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
michael@0 118 if (ERROR_SUCCESS != err_code)
michael@0 119 return err_code;
michael@0 120 }
michael@0 121
michael@0 122 restricted_token.SetIntegrityLevel(integrity_level);
michael@0 123
michael@0 124 switch (token_type) {
michael@0 125 case PRIMARY: {
michael@0 126 err_code = restricted_token.GetRestrictedTokenHandle(token_handle);
michael@0 127 break;
michael@0 128 }
michael@0 129 case IMPERSONATION: {
michael@0 130 err_code = restricted_token.GetRestrictedTokenHandleForImpersonation(
michael@0 131 token_handle);
michael@0 132 break;
michael@0 133 }
michael@0 134 default: {
michael@0 135 err_code = ERROR_BAD_ARGUMENTS;
michael@0 136 break;
michael@0 137 }
michael@0 138 }
michael@0 139
michael@0 140 return err_code;
michael@0 141 }
michael@0 142
michael@0 143 DWORD StartRestrictedProcessInJob(wchar_t *command_line,
michael@0 144 TokenLevel primary_level,
michael@0 145 TokenLevel impersonation_level,
michael@0 146 JobLevel job_level,
michael@0 147 HANDLE *const job_handle_ret) {
michael@0 148 Job job;
michael@0 149 DWORD err_code = job.Init(job_level, NULL, 0);
michael@0 150 if (ERROR_SUCCESS != err_code)
michael@0 151 return err_code;
michael@0 152
michael@0 153 if (JOB_UNPROTECTED != job_level) {
michael@0 154 // Share the Desktop handle to be able to use MessageBox() in the sandboxed
michael@0 155 // application.
michael@0 156 err_code = job.UserHandleGrantAccess(GetDesktopWindow());
michael@0 157 if (ERROR_SUCCESS != err_code)
michael@0 158 return err_code;
michael@0 159 }
michael@0 160
michael@0 161 // Create the primary (restricted) token for the process
michael@0 162 HANDLE primary_token_handle = NULL;
michael@0 163 err_code = CreateRestrictedToken(&primary_token_handle,
michael@0 164 primary_level,
michael@0 165 INTEGRITY_LEVEL_LAST,
michael@0 166 PRIMARY);
michael@0 167 if (ERROR_SUCCESS != err_code) {
michael@0 168 return err_code;
michael@0 169 }
michael@0 170 base::win::ScopedHandle primary_token(primary_token_handle);
michael@0 171
michael@0 172 // Create the impersonation token (restricted) to be able to start the
michael@0 173 // process.
michael@0 174 HANDLE impersonation_token_handle;
michael@0 175 err_code = CreateRestrictedToken(&impersonation_token_handle,
michael@0 176 impersonation_level,
michael@0 177 INTEGRITY_LEVEL_LAST,
michael@0 178 IMPERSONATION);
michael@0 179 if (ERROR_SUCCESS != err_code) {
michael@0 180 return err_code;
michael@0 181 }
michael@0 182 base::win::ScopedHandle impersonation_token(impersonation_token_handle);
michael@0 183
michael@0 184 // Start the process
michael@0 185 STARTUPINFO startup_info = {0};
michael@0 186 base::win::ScopedProcessInformation process_info;
michael@0 187 DWORD flags = CREATE_SUSPENDED;
michael@0 188
michael@0 189 if (base::win::GetVersion() < base::win::VERSION_WIN8) {
michael@0 190 // Windows 8 implements nested jobs, but for older systems we need to
michael@0 191 // break out of any job we're in to enforce our restrictions.
michael@0 192 flags |= CREATE_BREAKAWAY_FROM_JOB;
michael@0 193 }
michael@0 194
michael@0 195 if (!::CreateProcessAsUser(primary_token.Get(),
michael@0 196 NULL, // No application name.
michael@0 197 command_line,
michael@0 198 NULL, // No security attribute.
michael@0 199 NULL, // No thread attribute.
michael@0 200 FALSE, // Do not inherit handles.
michael@0 201 flags,
michael@0 202 NULL, // Use the environment of the caller.
michael@0 203 NULL, // Use current directory of the caller.
michael@0 204 &startup_info,
michael@0 205 process_info.Receive())) {
michael@0 206 return ::GetLastError();
michael@0 207 }
michael@0 208
michael@0 209 // Change the token of the main thread of the new process for the
michael@0 210 // impersonation token with more rights.
michael@0 211 {
michael@0 212 HANDLE temp_thread = process_info.thread_handle();
michael@0 213 if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) {
michael@0 214 ::TerminateProcess(process_info.process_handle(),
michael@0 215 0); // exit code
michael@0 216 return ::GetLastError();
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 err_code = job.AssignProcessToJob(process_info.process_handle());
michael@0 221 if (ERROR_SUCCESS != err_code) {
michael@0 222 ::TerminateProcess(process_info.process_handle(),
michael@0 223 0); // exit code
michael@0 224 return ::GetLastError();
michael@0 225 }
michael@0 226
michael@0 227 // Start the application
michael@0 228 ::ResumeThread(process_info.thread_handle());
michael@0 229
michael@0 230 (*job_handle_ret) = job.Detach();
michael@0 231
michael@0 232 return ERROR_SUCCESS;
michael@0 233 }
michael@0 234
michael@0 235 DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type,
michael@0 236 const wchar_t* ace_access,
michael@0 237 const wchar_t* integrity_level_sid) {
michael@0 238 // Build the SDDL string for the label.
michael@0 239 std::wstring sddl = L"S:("; // SDDL for a SACL.
michael@0 240 sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
michael@0 241 sddl += L";;"; // No Ace Flags.
michael@0 242 sddl += ace_access; // Add the ACE access.
michael@0 243 sddl += L";;;"; // No ObjectType and Inherited Object Type.
michael@0 244 sddl += integrity_level_sid; // Trustee Sid.
michael@0 245 sddl += L")";
michael@0 246
michael@0 247 DWORD error = ERROR_SUCCESS;
michael@0 248 PSECURITY_DESCRIPTOR sec_desc = NULL;
michael@0 249
michael@0 250 PACL sacl = NULL;
michael@0 251 BOOL sacl_present = FALSE;
michael@0 252 BOOL sacl_defaulted = FALSE;
michael@0 253
michael@0 254 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
michael@0 255 SDDL_REVISION,
michael@0 256 &sec_desc, NULL)) {
michael@0 257 if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
michael@0 258 &sacl_defaulted)) {
michael@0 259 error = ::SetSecurityInfo(handle, type,
michael@0 260 LABEL_SECURITY_INFORMATION, NULL, NULL, NULL,
michael@0 261 sacl);
michael@0 262 } else {
michael@0 263 error = ::GetLastError();
michael@0 264 }
michael@0 265
michael@0 266 ::LocalFree(sec_desc);
michael@0 267 } else {
michael@0 268 return::GetLastError();
michael@0 269 }
michael@0 270
michael@0 271 return error;
michael@0 272 }
michael@0 273
michael@0 274 const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
michael@0 275 switch (integrity_level) {
michael@0 276 case INTEGRITY_LEVEL_SYSTEM:
michael@0 277 return L"S-1-16-16384";
michael@0 278 case INTEGRITY_LEVEL_HIGH:
michael@0 279 return L"S-1-16-12288";
michael@0 280 case INTEGRITY_LEVEL_MEDIUM:
michael@0 281 return L"S-1-16-8192";
michael@0 282 case INTEGRITY_LEVEL_MEDIUM_LOW:
michael@0 283 return L"S-1-16-6144";
michael@0 284 case INTEGRITY_LEVEL_LOW:
michael@0 285 return L"S-1-16-4096";
michael@0 286 case INTEGRITY_LEVEL_BELOW_LOW:
michael@0 287 return L"S-1-16-2048";
michael@0 288 case INTEGRITY_LEVEL_UNTRUSTED:
michael@0 289 return L"S-1-16-0";
michael@0 290 case INTEGRITY_LEVEL_LAST:
michael@0 291 return NULL;
michael@0 292 }
michael@0 293
michael@0 294 NOTREACHED();
michael@0 295 return NULL;
michael@0 296 }
michael@0 297 DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
michael@0 298 if (base::win::GetVersion() < base::win::VERSION_VISTA)
michael@0 299 return ERROR_SUCCESS;
michael@0 300
michael@0 301 const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level);
michael@0 302 if (!integrity_level_str) {
michael@0 303 // No mandatory level specified, we don't change it.
michael@0 304 return ERROR_SUCCESS;
michael@0 305 }
michael@0 306
michael@0 307 PSID integrity_sid = NULL;
michael@0 308 if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
michael@0 309 return ::GetLastError();
michael@0 310
michael@0 311 TOKEN_MANDATORY_LABEL label = {0};
michael@0 312 label.Label.Attributes = SE_GROUP_INTEGRITY;
michael@0 313 label.Label.Sid = integrity_sid;
michael@0 314
michael@0 315 DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid);
michael@0 316 BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label,
michael@0 317 size);
michael@0 318 ::LocalFree(integrity_sid);
michael@0 319
michael@0 320 return result ? ERROR_SUCCESS : ::GetLastError();
michael@0 321 }
michael@0 322
michael@0 323 DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
michael@0 324 if (base::win::GetVersion() < base::win::VERSION_VISTA)
michael@0 325 return ERROR_SUCCESS;
michael@0 326
michael@0 327 // We don't check for an invalid level here because we'll just let it
michael@0 328 // fail on the SetTokenIntegrityLevel call later on.
michael@0 329 if (integrity_level == INTEGRITY_LEVEL_LAST) {
michael@0 330 // No mandatory level specified, we don't change it.
michael@0 331 return ERROR_SUCCESS;
michael@0 332 }
michael@0 333
michael@0 334 HANDLE token_handle;
michael@0 335 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
michael@0 336 &token_handle))
michael@0 337 return ::GetLastError();
michael@0 338
michael@0 339 base::win::ScopedHandle token(token_handle);
michael@0 340
michael@0 341 return SetTokenIntegrityLevel(token.Get(), integrity_level);
michael@0 342 }
michael@0 343
michael@0 344 } // namespace sandbox

mercurial