access_token.cc (19629B)
1 // Copyright 2021 The Chromium Authors 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 "base/win/access_token.h" 6 7 #include <windows.h> 8 9 #include <memory> 10 #include <utility> 11 12 #include "base/numerics/checked_math.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 16 namespace base::win { 17 18 namespace { 19 20 // The SECURITY_IMPERSONATION_LEVEL type is an enum and therefore can't be 21 // forward declared in windows_types.h. Ensure our separate definition matches 22 // the existing values for simplicity. 23 static_assert(static_cast<int>(SecurityImpersonationLevel::kAnonymous) == 24 SecurityAnonymous); 25 static_assert(static_cast<int>(SecurityImpersonationLevel::kIdentification) == 26 SecurityIdentification); 27 static_assert(static_cast<int>(SecurityImpersonationLevel::kImpersonation) == 28 SecurityImpersonation); 29 static_assert(static_cast<int>(SecurityImpersonationLevel::kDelegation) == 30 SecurityDelegation); 31 32 typedef BOOL(WINAPI* CreateAppContainerTokenFunction)( 33 HANDLE TokenHandle, 34 PSECURITY_CAPABILITIES SecurityCapabilities, 35 PHANDLE OutToken); 36 37 Sid UnwrapSid(absl::optional<Sid>&& sid) { 38 DCHECK(sid); 39 return std::move(*sid); 40 } 41 42 absl::optional<std::vector<char>> GetTokenInfo( 43 HANDLE token, 44 TOKEN_INFORMATION_CLASS info_class) { 45 // Get the buffer size. The call to GetTokenInformation should never succeed. 46 DWORD size = 0; 47 if (::GetTokenInformation(token, info_class, nullptr, 0, &size) || !size) 48 return absl::nullopt; 49 50 std::vector<char> temp_buffer(size); 51 if (!::GetTokenInformation(token, info_class, temp_buffer.data(), size, 52 &size)) { 53 return absl::nullopt; 54 } 55 56 return std::move(temp_buffer); 57 } 58 59 template <typename T> 60 absl::optional<T> GetTokenInfoFixed(HANDLE token, 61 TOKEN_INFORMATION_CLASS info_class) { 62 T result; 63 DWORD size = sizeof(T); 64 if (!::GetTokenInformation(token, info_class, &result, size, &size)) 65 return absl::nullopt; 66 67 return result; 68 } 69 70 template <typename T> 71 T* GetType(absl::optional<std::vector<char>>& info) { 72 DCHECK(info); 73 DCHECK(info->size() >= sizeof(T)); 74 return reinterpret_cast<T*>(info->data()); 75 } 76 77 std::vector<AccessToken::Group> GetGroupsFromToken( 78 HANDLE token, 79 TOKEN_INFORMATION_CLASS info_class) { 80 absl::optional<std::vector<char>> groups = GetTokenInfo(token, info_class); 81 // Sometimes only the GroupCount field is returned which indicates an empty 82 // group set. If the buffer is smaller than the TOKEN_GROUPS structure then 83 // just return an empty vector. 84 if (!groups || (groups->size() < sizeof(TOKEN_GROUPS))) 85 return {}; 86 87 TOKEN_GROUPS* groups_ptr = GetType<TOKEN_GROUPS>(groups); 88 std::vector<AccessToken::Group> ret; 89 ret.reserve(groups_ptr->GroupCount); 90 for (DWORD index = 0; index < groups_ptr->GroupCount; ++index) { 91 ret.emplace_back(UnwrapSid(Sid::FromPSID(groups_ptr->Groups[index].Sid)), 92 groups_ptr->Groups[index].Attributes); 93 } 94 return ret; 95 } 96 97 TOKEN_STATISTICS GetTokenStatistics(HANDLE token) { 98 absl::optional<TOKEN_STATISTICS> value = 99 GetTokenInfoFixed<TOKEN_STATISTICS>(token, TokenStatistics); 100 if (!value) 101 return {}; 102 return *value; 103 } 104 105 CHROME_LUID ConvertLuid(const LUID& luid) { 106 CHROME_LUID ret; 107 ret.LowPart = luid.LowPart; 108 ret.HighPart = luid.HighPart; 109 return ret; 110 } 111 112 HANDLE DuplicateToken(HANDLE token, 113 ACCESS_MASK desired_access, 114 SECURITY_IMPERSONATION_LEVEL imp_level, 115 TOKEN_TYPE type) { 116 HANDLE new_token; 117 if (!::DuplicateTokenEx(token, TOKEN_QUERY | desired_access, nullptr, 118 imp_level, type, &new_token)) { 119 return nullptr; 120 } 121 return new_token; 122 } 123 124 std::vector<SID_AND_ATTRIBUTES> ConvertSids(const std::vector<Sid>& sids, 125 DWORD attributes) { 126 std::vector<SID_AND_ATTRIBUTES> ret; 127 ret.reserve(sids.size()); 128 for (const Sid& sid : sids) { 129 SID_AND_ATTRIBUTES entry = {}; 130 entry.Sid = sid.GetPSID(); 131 entry.Attributes = attributes; 132 ret.push_back(entry); 133 } 134 return ret; 135 } 136 137 absl::optional<LUID> LookupPrivilege(const std::wstring& name) { 138 LUID luid; 139 if (!::LookupPrivilegeValue(nullptr, name.c_str(), &luid)) { 140 return absl::nullopt; 141 } 142 return luid; 143 } 144 145 std::vector<LUID_AND_ATTRIBUTES> ConvertPrivileges( 146 const std::vector<std::wstring>& privs, 147 DWORD attributes) { 148 std::vector<LUID_AND_ATTRIBUTES> ret; 149 ret.reserve(privs.size()); 150 for (const std::wstring& priv : privs) { 151 absl::optional<LUID> luid = LookupPrivilege(priv); 152 if (!luid) { 153 return {}; 154 } 155 LUID_AND_ATTRIBUTES entry = {}; 156 entry.Luid = *luid; 157 entry.Attributes = attributes; 158 ret.push_back(entry); 159 } 160 return ret; 161 } 162 163 template <typename T> 164 T* GetPointer(std::vector<T>& values) { 165 if (values.empty()) { 166 return nullptr; 167 } 168 return values.data(); 169 } 170 171 template <typename T> 172 bool Set(const ScopedHandle& token, 173 TOKEN_INFORMATION_CLASS info_class, 174 T& value) { 175 return !!::SetTokenInformation(token.get(), info_class, &value, 176 sizeof(value)); 177 } 178 179 absl::optional<DWORD> AdjustPrivilege(const ScopedHandle& token, 180 const std::wstring& priv, 181 DWORD attributes) { 182 TOKEN_PRIVILEGES token_privs = {}; 183 token_privs.PrivilegeCount = 1; 184 absl::optional<LUID> luid = LookupPrivilege(priv); 185 if (!luid) { 186 return absl::nullopt; 187 } 188 token_privs.Privileges[0].Luid = *luid; 189 token_privs.Privileges[0].Attributes = attributes; 190 191 TOKEN_PRIVILEGES out_privs = {}; 192 DWORD out_length = 0; 193 if (!::AdjustTokenPrivileges(token.get(), FALSE, &token_privs, 194 sizeof(out_privs), &out_privs, &out_length)) { 195 return absl::nullopt; 196 } 197 if (::GetLastError() == ERROR_NOT_ALL_ASSIGNED) { 198 return absl::nullopt; 199 } 200 if (out_privs.PrivilegeCount == 1) { 201 return out_privs.Privileges[0].Attributes; 202 } 203 return attributes; 204 } 205 } // namespace 206 207 bool AccessToken::Group::IsIntegrity() const { 208 return !!(attributes_ & SE_GROUP_INTEGRITY); 209 } 210 211 bool AccessToken::Group::IsEnabled() const { 212 return !!(attributes_ & SE_GROUP_ENABLED); 213 } 214 215 bool AccessToken::Group::IsDenyOnly() const { 216 return !!(attributes_ & SE_GROUP_USE_FOR_DENY_ONLY); 217 } 218 219 bool AccessToken::Group::IsLogonId() const { 220 return (attributes_ & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID; 221 } 222 223 AccessToken::Group::Group(Sid&& sid, DWORD attributes) 224 : sid_(std::move(sid)), attributes_(attributes) {} 225 AccessToken::Group::Group(Group&&) = default; 226 AccessToken::Group::Group::~Group() = default; 227 228 std::wstring AccessToken::Privilege::GetName() const { 229 WCHAR name[128]; 230 LUID luid; 231 luid.LowPart = luid_.LowPart; 232 luid.HighPart = luid_.HighPart; 233 DWORD size = std::size(name); 234 return ::LookupPrivilegeName(nullptr, &luid, name, &size) 235 ? name 236 : ASCIIToWide( 237 StringPrintf("%08lX-%08lX", luid.HighPart, luid.LowPart)); 238 } 239 240 bool AccessToken::Privilege::IsEnabled() const { 241 return !!(attributes_ & SE_PRIVILEGE_ENABLED); 242 } 243 244 AccessToken::Privilege::Privilege(CHROME_LUID luid, DWORD attributes) 245 : luid_(luid), attributes_(attributes) {} 246 247 absl::optional<AccessToken> AccessToken::FromToken(HANDLE token, 248 ACCESS_MASK desired_access) { 249 HANDLE new_token; 250 if (!::DuplicateHandle(::GetCurrentProcess(), token, ::GetCurrentProcess(), 251 &new_token, TOKEN_QUERY | desired_access, FALSE, 0)) { 252 return absl::nullopt; 253 } 254 return AccessToken(new_token); 255 } 256 257 absl::optional<AccessToken> AccessToken::FromToken(ScopedHandle&& token) { 258 if (!token.is_valid()) { 259 ::SetLastError(ERROR_INVALID_HANDLE); 260 return absl::nullopt; 261 } 262 if (!GetTokenInfoFixed<TOKEN_STATISTICS>(token.get(), TokenStatistics)) { 263 return absl::nullopt; 264 } 265 return AccessToken(token.release()); 266 } 267 268 absl::optional<AccessToken> AccessToken::FromProcess( 269 HANDLE process, 270 bool impersonation, 271 ACCESS_MASK desired_access) { 272 HANDLE token = nullptr; 273 if (impersonation) { 274 if (!::OpenProcessToken(process, TOKEN_DUPLICATE, &token)) 275 return absl::nullopt; 276 ScopedHandle primary_token(token); 277 token = DuplicateToken(primary_token.get(), desired_access, 278 SecurityIdentification, TokenImpersonation); 279 if (!token) { 280 return absl::nullopt; 281 } 282 } else { 283 if (!::OpenProcessToken(process, TOKEN_QUERY | desired_access, &token)) 284 return absl::nullopt; 285 } 286 return AccessToken(token); 287 } 288 289 absl::optional<AccessToken> AccessToken::FromCurrentProcess( 290 bool impersonation, 291 ACCESS_MASK desired_access) { 292 return FromProcess(::GetCurrentProcess(), impersonation, desired_access); 293 } 294 295 absl::optional<AccessToken> AccessToken::FromThread( 296 HANDLE thread, 297 bool open_as_self, 298 ACCESS_MASK desired_access) { 299 HANDLE token; 300 if (!::OpenThreadToken(thread, TOKEN_QUERY | desired_access, open_as_self, 301 &token)) 302 return absl::nullopt; 303 return AccessToken(token); 304 } 305 306 absl::optional<AccessToken> AccessToken::FromCurrentThread( 307 bool open_as_self, 308 ACCESS_MASK desired_access) { 309 return FromThread(::GetCurrentThread(), open_as_self, desired_access); 310 } 311 312 absl::optional<AccessToken> AccessToken::FromEffective( 313 ACCESS_MASK desired_access) { 314 absl::optional<AccessToken> token = FromCurrentThread(true, desired_access); 315 if (token) 316 return token; 317 if (::GetLastError() != ERROR_NO_TOKEN) 318 return absl::nullopt; 319 return FromCurrentProcess(false, desired_access); 320 } 321 322 AccessToken::AccessToken(AccessToken&&) = default; 323 AccessToken& AccessToken::operator=(AccessToken&&) = default; 324 AccessToken::~AccessToken() = default; 325 326 Sid AccessToken::User() const { 327 return UserGroup().GetSid().Clone(); 328 } 329 330 AccessToken::Group AccessToken::UserGroup() const { 331 absl::optional<std::vector<char>> buffer = 332 GetTokenInfo(token_.get(), TokenUser); 333 SID_AND_ATTRIBUTES& user = GetType<TOKEN_USER>(buffer)->User; 334 return {UnwrapSid(Sid::FromPSID(user.Sid)), user.Attributes}; 335 } 336 337 Sid AccessToken::Owner() const { 338 absl::optional<std::vector<char>> buffer = 339 GetTokenInfo(token_.get(), TokenOwner); 340 return UnwrapSid(Sid::FromPSID(GetType<TOKEN_OWNER>(buffer)->Owner)); 341 } 342 343 Sid AccessToken::PrimaryGroup() const { 344 absl::optional<std::vector<char>> buffer = 345 GetTokenInfo(token_.get(), TokenPrimaryGroup); 346 return UnwrapSid( 347 Sid::FromPSID(GetType<TOKEN_PRIMARY_GROUP>(buffer)->PrimaryGroup)); 348 } 349 350 absl::optional<Sid> AccessToken::LogonId() const { 351 std::vector<AccessToken::Group> groups = 352 GetGroupsFromToken(token_.get(), TokenLogonSid); 353 for (const AccessToken::Group& group : groups) { 354 if (group.IsLogonId()) 355 return group.GetSid().Clone(); 356 } 357 return absl::nullopt; 358 } 359 360 DWORD AccessToken::IntegrityLevel() const { 361 absl::optional<std::vector<char>> buffer = 362 GetTokenInfo(token_.get(), TokenIntegrityLevel); 363 if (!buffer) 364 return MAXDWORD; 365 366 PSID il_sid = GetType<TOKEN_MANDATORY_LABEL>(buffer)->Label.Sid; 367 return *::GetSidSubAuthority( 368 il_sid, static_cast<DWORD>(*::GetSidSubAuthorityCount(il_sid) - 1)); 369 } 370 371 bool AccessToken::SetIntegrityLevel(DWORD integrity_level) { 372 absl::optional<base::win::Sid> sid = Sid::FromIntegrityLevel(integrity_level); 373 if (!sid) { 374 ::SetLastError(ERROR_INVALID_SID); 375 return false; 376 } 377 378 TOKEN_MANDATORY_LABEL label = {}; 379 label.Label.Attributes = SE_GROUP_INTEGRITY; 380 label.Label.Sid = sid->GetPSID(); 381 return Set(token_, TokenIntegrityLevel, label); 382 } 383 384 DWORD AccessToken::SessionId() const { 385 absl::optional<DWORD> value = 386 GetTokenInfoFixed<DWORD>(token_.get(), TokenSessionId); 387 if (!value) 388 return MAXDWORD; 389 return *value; 390 } 391 392 std::vector<AccessToken::Group> AccessToken::Groups() const { 393 return GetGroupsFromToken(token_.get(), TokenGroups); 394 } 395 396 bool AccessToken::IsRestricted() const { 397 return !!::IsTokenRestricted(token_.get()); 398 } 399 400 std::vector<AccessToken::Group> AccessToken::RestrictedSids() const { 401 return GetGroupsFromToken(token_.get(), TokenRestrictedSids); 402 } 403 404 bool AccessToken::IsAppContainer() const { 405 absl::optional<DWORD> value = 406 GetTokenInfoFixed<DWORD>(token_.get(), TokenIsAppContainer); 407 if (!value) 408 return false; 409 return !!*value; 410 } 411 412 absl::optional<Sid> AccessToken::AppContainerSid() const { 413 absl::optional<std::vector<char>> buffer = 414 GetTokenInfo(token_.get(), TokenAppContainerSid); 415 if (!buffer) 416 return absl::nullopt; 417 418 TOKEN_APPCONTAINER_INFORMATION* info = 419 GetType<TOKEN_APPCONTAINER_INFORMATION>(buffer); 420 if (!info->TokenAppContainer) 421 return absl::nullopt; 422 return Sid::FromPSID(info->TokenAppContainer); 423 } 424 425 std::vector<AccessToken::Group> AccessToken::Capabilities() const { 426 return GetGroupsFromToken(token_.get(), TokenCapabilities); 427 } 428 429 absl::optional<AccessToken> AccessToken::LinkedToken() const { 430 absl::optional<TOKEN_LINKED_TOKEN> value = 431 GetTokenInfoFixed<TOKEN_LINKED_TOKEN>(token_.get(), TokenLinkedToken); 432 if (!value) 433 return absl::nullopt; 434 return AccessToken(value->LinkedToken); 435 } 436 437 absl::optional<AccessControlList> AccessToken::DefaultDacl() const { 438 absl::optional<std::vector<char>> dacl_buffer = 439 GetTokenInfo(token_.get(), TokenDefaultDacl); 440 if (!dacl_buffer) 441 return absl::nullopt; 442 TOKEN_DEFAULT_DACL* dacl_ptr = GetType<TOKEN_DEFAULT_DACL>(dacl_buffer); 443 return AccessControlList::FromPACL(dacl_ptr->DefaultDacl); 444 } 445 446 bool AccessToken::SetDefaultDacl(const AccessControlList& default_dacl) { 447 TOKEN_DEFAULT_DACL set_default_dacl = {}; 448 set_default_dacl.DefaultDacl = default_dacl.get(); 449 return Set(token_, TokenDefaultDacl, set_default_dacl); 450 } 451 452 CHROME_LUID AccessToken::Id() const { 453 return ConvertLuid(GetTokenStatistics(token_.get()).TokenId); 454 } 455 456 CHROME_LUID AccessToken::AuthenticationId() const { 457 return ConvertLuid(GetTokenStatistics(token_.get()).AuthenticationId); 458 } 459 460 std::vector<AccessToken::Privilege> AccessToken::Privileges() const { 461 absl::optional<std::vector<char>> privileges = 462 GetTokenInfo(token_.get(), TokenPrivileges); 463 if (!privileges) 464 return {}; 465 TOKEN_PRIVILEGES* privileges_ptr = GetType<TOKEN_PRIVILEGES>(privileges); 466 std::vector<AccessToken::Privilege> ret; 467 ret.reserve(privileges_ptr->PrivilegeCount); 468 for (DWORD index = 0; index < privileges_ptr->PrivilegeCount; ++index) { 469 ret.emplace_back(ConvertLuid(privileges_ptr->Privileges[index].Luid), 470 privileges_ptr->Privileges[index].Attributes); 471 } 472 return ret; 473 } 474 475 bool AccessToken::IsElevated() const { 476 absl::optional<TOKEN_ELEVATION> value = 477 GetTokenInfoFixed<TOKEN_ELEVATION>(token_.get(), TokenElevation); 478 if (!value) 479 return false; 480 return !!value->TokenIsElevated; 481 } 482 483 bool AccessToken::IsMember(const Sid& sid) const { 484 BOOL is_member = FALSE; 485 return ::CheckTokenMembership(token_.get(), sid.GetPSID(), &is_member) && 486 !!is_member; 487 } 488 489 bool AccessToken::IsMember(WellKnownSid known_sid) const { 490 return IsMember(Sid(known_sid)); 491 } 492 493 bool AccessToken::IsImpersonation() const { 494 return GetTokenStatistics(token_.get()).TokenType == TokenImpersonation; 495 } 496 497 bool AccessToken::IsIdentification() const { 498 return ImpersonationLevel() < SecurityImpersonationLevel::kImpersonation; 499 } 500 501 SecurityImpersonationLevel AccessToken::ImpersonationLevel() const { 502 TOKEN_STATISTICS stats = GetTokenStatistics(token_.get()); 503 if (stats.TokenType != TokenImpersonation) { 504 return SecurityImpersonationLevel::kImpersonation; 505 } 506 507 return static_cast<SecurityImpersonationLevel>( 508 GetTokenStatistics(token_.get()).ImpersonationLevel); 509 } 510 511 absl::optional<AccessToken> AccessToken::DuplicatePrimary( 512 ACCESS_MASK desired_access) const { 513 HANDLE token = DuplicateToken(token_.get(), desired_access, SecurityAnonymous, 514 TokenPrimary); 515 if (!token) { 516 return absl::nullopt; 517 } 518 return AccessToken{token}; 519 } 520 521 absl::optional<AccessToken> AccessToken::DuplicateImpersonation( 522 SecurityImpersonationLevel impersonation_level, 523 ACCESS_MASK desired_access) const { 524 HANDLE token = DuplicateToken( 525 token_.get(), desired_access, 526 static_cast<SECURITY_IMPERSONATION_LEVEL>(impersonation_level), 527 TokenImpersonation); 528 if (!token) { 529 return absl::nullopt; 530 } 531 return AccessToken(token); 532 } 533 534 absl::optional<AccessToken> AccessToken::CreateRestricted( 535 DWORD flags, 536 const std::vector<Sid>& sids_to_disable, 537 const std::vector<std::wstring>& privileges_to_delete, 538 const std::vector<Sid>& sids_to_restrict, 539 ACCESS_MASK desired_access) const { 540 std::vector<SID_AND_ATTRIBUTES> sids_to_disable_buf = 541 ConvertSids(sids_to_disable, 0); 542 std::vector<SID_AND_ATTRIBUTES> sids_to_restrict_buf = 543 ConvertSids(sids_to_restrict, 0); 544 std::vector<LUID_AND_ATTRIBUTES> privileges_to_delete_buf = 545 ConvertPrivileges(privileges_to_delete, 0); 546 if (privileges_to_delete_buf.size() != privileges_to_delete.size()) { 547 return absl::nullopt; 548 } 549 550 HANDLE token; 551 if (!::CreateRestrictedToken( 552 token_.get(), flags, checked_cast<DWORD>(sids_to_disable_buf.size()), 553 GetPointer(sids_to_disable_buf), 554 checked_cast<DWORD>(privileges_to_delete_buf.size()), 555 GetPointer(privileges_to_delete_buf), 556 checked_cast<DWORD>(sids_to_restrict_buf.size()), 557 GetPointer(sids_to_restrict_buf), &token)) { 558 return absl::nullopt; 559 } 560 561 ScopedHandle token_handle(token); 562 return FromToken(token_handle.get(), desired_access); 563 } 564 565 absl::optional<AccessToken> AccessToken::CreateAppContainer( 566 const Sid& appcontainer_sid, 567 const std::vector<Sid>& capabilities, 568 ACCESS_MASK desired_access) const { 569 static const CreateAppContainerTokenFunction CreateAppContainerToken = 570 reinterpret_cast<CreateAppContainerTokenFunction>(::GetProcAddress( 571 ::GetModuleHandle(L"kernelbase.dll"), "CreateAppContainerToken")); 572 if (!CreateAppContainerToken) { 573 ::SetLastError(ERROR_PROC_NOT_FOUND); 574 return absl::nullopt; 575 } 576 577 std::vector<SID_AND_ATTRIBUTES> capabilities_buf = 578 ConvertSids(capabilities, SE_GROUP_ENABLED); 579 SECURITY_CAPABILITIES security_capabilities = {}; 580 security_capabilities.AppContainerSid = appcontainer_sid.GetPSID(); 581 security_capabilities.Capabilities = GetPointer(capabilities_buf); 582 security_capabilities.CapabilityCount = 583 checked_cast<DWORD>(capabilities_buf.size()); 584 585 HANDLE token = nullptr; 586 if (!CreateAppContainerToken(token_.get(), &security_capabilities, &token)) { 587 return absl::nullopt; 588 } 589 590 ScopedHandle token_handle(token); 591 return FromToken(token_handle.get(), desired_access); 592 } 593 594 absl::optional<bool> AccessToken::SetPrivilege(const std::wstring& name, 595 bool enable) { 596 absl::optional<DWORD> attrs = 597 AdjustPrivilege(token_, name.c_str(), enable ? SE_PRIVILEGE_ENABLED : 0); 598 if (!attrs) { 599 return absl::nullopt; 600 } 601 return !!(*attrs & SE_PRIVILEGE_ENABLED); 602 } 603 604 bool AccessToken::RemovePrivilege(const std::wstring& name) { 605 return AdjustPrivilege(token_, name.c_str(), SE_PRIVILEGE_REMOVED) 606 .has_value(); 607 } 608 609 bool AccessToken::is_valid() const { 610 return token_.is_valid(); 611 } 612 613 HANDLE AccessToken::get() const { 614 return token_.get(); 615 } 616 617 ScopedHandle AccessToken::release() { 618 return ScopedHandle(token_.release()); 619 } 620 621 AccessToken::AccessToken(HANDLE token) : token_(token) {} 622 623 } // namespace base::win