Wed, 31 Dec 2014 06:09:35 +0100
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 |