| 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 |