Security.cpp (4934B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "winget/Security.h" 5 #include "AppInstallerLogging.h" 6 #include "AppInstallerLanguageUtilities.h" 7 8 namespace AppInstaller::Security 9 { 10 namespace 11 { 12 bool IsSameAuthority(const SID_IDENTIFIER_AUTHORITY& a, const SID_IDENTIFIER_AUTHORITY& b) 13 { 14 for (size_t i = 0; i < ARRAYSIZE(a.Value); ++i) 15 { 16 if (a.Value[i] != b.Value[i]) 17 { 18 return false; 19 } 20 } 21 22 return true; 23 } 24 25 // Helper to impersonate the COM or RPC caller. 26 struct ImpersonateCOMorRPCCaller 27 { 28 static ImpersonateCOMorRPCCaller BeginImpersonation() 29 { 30 return {}; 31 } 32 33 ~ImpersonateCOMorRPCCaller() 34 { 35 if (m_serverSecurity) 36 { 37 FAIL_FAST_IF_FAILED(m_serverSecurity->RevertToSelf()); 38 } 39 else 40 { 41 FAIL_FAST_IF(RpcRevertToSelf() != RPC_S_OK); 42 } 43 } 44 45 private: 46 ImpersonateCOMorRPCCaller() 47 { 48 if (SUCCEEDED_LOG(CoGetCallContext(IID_IServerSecurity, m_serverSecurity.put_void()))) 49 { 50 THROW_IF_FAILED(m_serverSecurity->ImpersonateClient()); 51 } 52 else 53 { 54 RPC_STATUS status = RpcImpersonateClient(nullptr); 55 THROW_HR_IF(MAKE_HRESULT(SEVERITY_ERROR, FACILITY_RPC, status), status != RPC_S_OK); 56 } 57 } 58 59 wil::com_ptr<IServerSecurity> m_serverSecurity; 60 }; 61 } 62 63 IntegrityLevel GetEffectiveIntegrityLevel() 64 { 65 auto currentIntegrityLevel = wil::get_token_information<TOKEN_MANDATORY_LABEL>(); 66 PSID sid = currentIntegrityLevel->Label.Sid; 67 THROW_HR_IF(CO_E_INVALIDSID, !IsValidSid(sid)); 68 69 auto identifierAuthority = GetSidIdentifierAuthority(sid); 70 THROW_HR_IF(E_UNEXPECTED, !IsSameAuthority(*identifierAuthority, SECURITY_MANDATORY_LABEL_AUTHORITY)); 71 72 PUCHAR subAuthorityCount = GetSidSubAuthorityCount(sid); 73 THROW_HR_IF(E_UNEXPECTED, *subAuthorityCount != 1); 74 75 PDWORD subAuthority = GetSidSubAuthority(sid, 0); 76 77 switch (*subAuthority) 78 { 79 case SECURITY_MANDATORY_UNTRUSTED_RID: return IntegrityLevel::Untrusted; 80 case SECURITY_MANDATORY_LOW_RID: return IntegrityLevel::Low; 81 case SECURITY_MANDATORY_MEDIUM_RID: return IntegrityLevel::Medium; 82 case SECURITY_MANDATORY_HIGH_RID: return IntegrityLevel::High; 83 case SECURITY_MANDATORY_SYSTEM_RID: return IntegrityLevel::System; 84 case SECURITY_MANDATORY_PROTECTED_PROCESS_RID: return IntegrityLevel::ProtectedProcess; 85 } 86 87 THROW_HR(E_UNEXPECTED); 88 } 89 90 bool IsCOMCallerSameUserAndIntegrityLevel() 91 { 92 auto serverUser = wil::get_token_information<TOKEN_USER>(); 93 IntegrityLevel serverIntegrityLevel = GetEffectiveIntegrityLevel(); 94 95 auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation(); 96 97 auto callingUser = wil::get_token_information<TOKEN_USER>(); 98 IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel(); 99 100 if (!EqualSid(serverUser->User.Sid, callingUser->User.Sid)) 101 { 102 AICLI_LOG(Core, Crit, << "Attempt to access by another user: " << ToString(callingUser->User.Sid)); 103 return false; 104 } 105 106 if (ToIntegral(callingIntegrityLevel) < ToIntegral(serverIntegrityLevel)) 107 { 108 AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process: " << callingIntegrityLevel << " < " << serverIntegrityLevel); 109 return false; 110 } 111 112 return true; 113 } 114 115 bool IsCOMCallerIntegrityLevelAtLeast(IntegrityLevel minimumLevel) 116 { 117 auto impersonation = ImpersonateCOMorRPCCaller::BeginImpersonation(); 118 return IsCurrentIntegrityLevelAtLeast(minimumLevel); 119 } 120 121 bool IsCurrentIntegrityLevelAtLeast(IntegrityLevel minimumLevel) 122 { 123 IntegrityLevel callingIntegrityLevel = GetEffectiveIntegrityLevel(); 124 125 if (ToIntegral(callingIntegrityLevel) < ToIntegral(minimumLevel)) 126 { 127 AICLI_LOG(Core, Crit, << "Attempt to access by a lower integrity process than required: " << callingIntegrityLevel << " < " << minimumLevel); 128 return false; 129 } 130 131 return true; 132 } 133 134 std::string ToString(PSID sid) 135 { 136 wil::unique_hlocal_ansistring result; 137 THROW_IF_WIN32_BOOL_FALSE(ConvertSidToStringSidA(sid, &result)); 138 return result.get(); 139 } 140 }