Helpers.cpp (8140B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include <wil/resource.h> 5 #include <wil/win32_helpers.h> 6 #include <winrt/Windows.Security.Authorization.AppCapabilityAccess.h> 7 #include <appmodel.h> 8 #include <Helpers.h> 9 #include <winget/Filesystem.h> 10 #include <winget/Security.h> 11 #include <AppInstallerRuntime.h> 12 #include <AppInstallerLogging.h> 13 14 using namespace std::string_literals; 15 using namespace std::string_view_literals; 16 17 namespace winrt::Microsoft::Management::Deployment::implementation 18 { 19 namespace 20 { 21 static std::optional<std::string> s_callerName; 22 static wil::srwlock s_callerNameLock; 23 } 24 25 void SetComCallerName(std::string name) 26 { 27 auto lock = s_callerNameLock.lock_exclusive(); 28 s_callerName.emplace(std::move(name)); 29 } 30 31 std::string GetComCallerName(std::string defaultNameIfNotSet) 32 { 33 auto lock = s_callerNameLock.lock_shared(); 34 return s_callerName.has_value() ? s_callerName.value() : defaultNameIfNotSet; 35 } 36 37 std::pair<HRESULT, DWORD> GetCallerProcessId() 38 { 39 RPC_STATUS rpcStatus = RPC_S_OK; 40 RPC_CALL_ATTRIBUTES callAttributes = {}; 41 callAttributes.Version = RPC_CALL_ATTRIBUTES_VERSION; 42 callAttributes.Flags = RPC_QUERY_CLIENT_PID; 43 rpcStatus = RpcServerInqCallAttributes(nullptr, &callAttributes); 44 45 if (rpcStatus == RPC_S_NO_CALL_ACTIVE || 46 (rpcStatus == RPC_S_OK && HandleToULong(callAttributes.ClientPID) == GetCurrentProcessId())) 47 { 48 // in-proc is supported now. 49 return { S_OK, GetCurrentProcessId() }; 50 } 51 else if (rpcStatus == RPC_S_OK) 52 { 53 // out-of-proc case. 54 return { S_OK, HandleToULong(callAttributes.ClientPID) }; 55 } 56 else 57 { 58 return { E_ACCESSDENIED, 0 }; 59 } 60 } 61 62 std::wstring_view GetStringForCapability(Capability capability) 63 { 64 switch (capability) 65 { 66 case Capability::PackageManagement: 67 return L"packageManagement"sv; 68 case Capability::PackageQuery: 69 return L"packageQuery"sv; 70 default: 71 winrt::throw_hresult(E_UNEXPECTED); 72 } 73 } 74 75 HRESULT EnsureProcessHasCapability(Capability requiredCapability, DWORD callerProcessId) 76 { 77 bool allowed = false; 78 79 if (winrt::Windows::Foundation::Metadata::ApiInformation::IsTypePresent(winrt::name_of<winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapability>())) 80 { 81 // Get the caller process id and use it to check if the caller has permissions to access the feature. 82 winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus status = winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::DeniedBySystem; 83 84 winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapability capability{ nullptr }; 85 86 try 87 { 88 capability = winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapability::CreateWithProcessIdForUser(nullptr, GetStringForCapability(requiredCapability), callerProcessId); 89 } 90 catch (const winrt::hresult_invalid_argument&) 91 { 92 } 93 94 if (capability) 95 { 96 status = capability.CheckAccess(); 97 98 return ((status == winrt::Windows::Security::Authorization::AppCapabilityAccess::AppCapabilityAccessStatus::Allowed) ? S_OK : E_ACCESSDENIED); 99 } 100 } 101 102 // If AppCapability is not present, require at least medium IL callers 103 auto requiredIntegrityLevel = AppInstaller::Security::IntegrityLevel::Medium; 104 105 if (callerProcessId != GetCurrentProcessId()) 106 { 107 allowed = AppInstaller::Security::IsCOMCallerIntegrityLevelAtLeast(requiredIntegrityLevel); 108 } 109 else 110 { 111 allowed = AppInstaller::Security::IsCurrentIntegrityLevelAtLeast(requiredIntegrityLevel); 112 } 113 114 return (allowed ? S_OK : E_ACCESSDENIED); 115 } 116 117 HRESULT EnsureComCallerHasCapability(Capability requiredCapability) 118 { 119 auto [hr, callerProcessId] = GetCallerProcessId(); 120 RETURN_IF_FAILED(hr); 121 hr = EnsureProcessHasCapability(requiredCapability, callerProcessId); 122 // The Windows.Management.Deployment API has set the precedent that packageManagement is a superset of packageQuery 123 // and packageQuery does not need to be declared separately. 124 if (FAILED(hr) && requiredCapability == Capability::PackageQuery) 125 { 126 hr = EnsureProcessHasCapability(Capability::PackageManagement, callerProcessId); 127 } 128 RETURN_HR(hr); 129 } 130 131 // Best effort at getting caller info. This should only be used for logging. 132 std::wstring TryGetCallerProcessInfo(DWORD callerProcessId) 133 { 134 wil::unique_process_handle processHandle(OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, callerProcessId)); 135 if (processHandle) 136 { 137 WCHAR packageFamilyName[PACKAGE_FAMILY_NAME_MAX_LENGTH]{}; 138 UINT32 length = ARRAYSIZE(packageFamilyName); 139 if (::GetPackageFamilyName(processHandle.get(), &length, packageFamilyName) == ERROR_SUCCESS) 140 { 141 // If the package is calling into itself, fall through to the executable name 142 if (AppInstaller::Runtime::GetPackageFamilyName() != packageFamilyName) 143 { 144 return { packageFamilyName }; 145 } 146 } 147 148 // if the caller doesn't have an AppUserModelID then fall back to the executable name 149 std::filesystem::path executablePath = AppInstaller::Filesystem::GetExecutablePathForProcess(processHandle.get()); 150 if (executablePath.has_filename()) 151 { 152 return executablePath.filename(); 153 } 154 else if (!executablePath.empty()) 155 { 156 AICLI_LOG(Fail, Error, << "Unable to get valid executable for process ID [" << callerProcessId << "]: " << executablePath); 157 } 158 } 159 160 return {}; 161 } 162 163 std::string GetCallerName() 164 { 165 // See if caller name is set by caller 166 std::string callerName = GetComCallerName(""); 167 168 // Get process string 169 if (callerName.empty()) 170 { 171 try 172 { 173 auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); 174 if (SUCCEEDED(hrGetCallerId)) 175 { 176 callerName = AppInstaller::Utility::ConvertToUTF8(TryGetCallerProcessInfo(callerProcessId)); 177 } 178 } 179 CATCH_LOG(); 180 } 181 182 if (callerName.empty()) 183 { 184 callerName = "UnknownComCaller"; 185 } 186 187 return callerName; 188 } 189 190 bool IsBackgroundProcessForPolicy() 191 { 192 bool isBackgroundProcessForPolicy = false; 193 try 194 { 195 auto [hrGetCallerId, callerProcessId] = GetCallerProcessId(); 196 if (SUCCEEDED(hrGetCallerId) && callerProcessId != GetCurrentProcessId()) 197 { 198 // OutOfProc case, we check for explorer.exe 199 auto callerNameWide = AppInstaller::Utility::ConvertToUTF16(GetCallerName()); 200 auto processName = AppInstaller::Utility::ConvertToUTF8(std::filesystem::path{ callerNameWide }.filename().wstring()); 201 if (::AppInstaller::Utility::CaseInsensitiveEquals("explorer.exe", processName) || 202 ::AppInstaller::Utility::CaseInsensitiveEquals("taskhostw.exe", processName)) 203 { 204 isBackgroundProcessForPolicy = true; 205 } 206 } 207 } 208 CATCH_LOG(); 209 210 return isBackgroundProcessForPolicy; 211 } 212 }