GroupPolicy.cpp (18172B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "winget/GroupPolicy.h" 5 #include "AppInstallerLogging.h" 6 7 using namespace AppInstaller::StringResource; 8 9 namespace AppInstaller::Settings 10 { 11 namespace 12 { 13 const GroupPolicy& InstanceInternal(std::optional<GroupPolicy*> overridePolicy = {}) 14 { 15 const static GroupPolicy s_groupPolicy{ Registry::Key::OpenIfExists(HKEY_LOCAL_MACHINE, "Software\\Policies\\Microsoft\\Windows\\AppInstaller") }; 16 static GroupPolicy* s_override = nullptr; 17 18 if (overridePolicy.has_value()) 19 { 20 s_override = overridePolicy.value(); 21 } 22 23 return (s_override ? *s_override : s_groupPolicy); 24 } 25 26 std::optional<Registry::Value> GetRegistryValueObject(const Registry::Key& key, const std::string_view valueName) 27 { 28 if (!key) 29 { 30 // Key does not exist; there's nothing to return 31 return std::nullopt; 32 } 33 34 return key[valueName]; 35 } 36 37 template<Registry::Value::Type T> 38 std::optional<decltype(std::declval<Registry::Value>().GetValue<T>())> GetRegistryValueData(const Registry::Value& regValue, const std::string_view valueName) 39 { 40 auto value = regValue.TryGetValue<T>(); 41 if (!value.has_value()) 42 { 43 AICLI_LOG(Core, Warning, << "Value for policy '" << valueName << "' does not have expected type"); 44 return std::nullopt; 45 } 46 47 return std::move(value.value()); 48 } 49 50 template<Registry::Value::Type T> 51 std::optional<decltype(std::declval<Registry::Value>().GetValue<T>())> GetRegistryValueData(const Registry::Key& key, const std::string_view valueName) 52 { 53 auto regValue = GetRegistryValueObject(key, valueName); 54 if (!regValue.has_value()) 55 { 56 // Value does not exist; there's nothing to return 57 return std::nullopt; 58 } 59 60 return GetRegistryValueData<T>(regValue.value(), valueName); 61 } 62 63 std::optional<bool> RegistryValueIsTrue(const Registry::Key& key, std::string_view valueName) 64 { 65 auto intValue = GetRegistryValueData<Registry::Value::Type::DWord>(key, valueName); 66 if (!intValue.has_value()) 67 { 68 return std::nullopt; 69 } 70 71 AICLI_LOG(Core, Verbose, << "Found policy '" << valueName << "', Value: " << *intValue); 72 return (bool)*intValue; 73 } 74 75 PolicyState GetStateInternal(const Registry::Key& key, TogglePolicy::Policy policy) 76 { 77 // Default to not configured if there is no policy for this 78 if (policy == TogglePolicy::Policy::None) 79 { 80 return PolicyState::NotConfigured; 81 } 82 83 auto togglePolicy = TogglePolicy::GetPolicy(policy); 84 85 // Policies are not configured if there is no registry value. 86 auto setting = RegistryValueIsTrue(key, togglePolicy.RegValueName()); 87 if (!setting.has_value()) 88 { 89 return PolicyState::NotConfigured; 90 } 91 92 // Return flag as-is or invert depending on the policy 93 return *setting ? PolicyState::Enabled : PolicyState::Disabled; 94 } 95 96 template <ValuePolicy P> 97 void Validate(const Registry::Key& policiesKey, GroupPolicy::ValuePoliciesMap& policies) 98 { 99 auto value = details::ValuePolicyMapping<P>::ReadAndValidate(policiesKey); 100 if (value.has_value()) 101 { 102 policies.Add<P>(std::move(*value)); 103 } 104 } 105 106 template <> 107 void Validate<ValuePolicy::None>(const Registry::Key&, GroupPolicy::ValuePoliciesMap&) {}; 108 109 template <size_t... P> 110 void ValidateAllValuePolicies( 111 const Registry::Key& policiesKey, 112 GroupPolicy::ValuePoliciesMap& policies, 113 std::index_sequence<P...>) 114 { 115 // Use folding to call each policy validate function. 116 (FoldHelper{}, ..., Validate<static_cast<ValuePolicy>(P)>(policiesKey, policies)); 117 } 118 119 // Reads a list from a Group Policy. 120 // The list is stored in a sub-key of the policies key, and each value in that key is a list item. 121 // Cases not considered by this function because we don't use them: 122 // - When the list is in an arbitrary key, not a sub key. 123 // - When the list values are mixed with other values and are identified by a prefix in their names. 124 // - When the value names are relevant. 125 template<ValuePolicy P> 126 std::optional<typename details::ValuePolicyMapping<P>::value_t> ReadList(const Registry::Key& policiesKey) 127 { 128 using Mapping = details::ValuePolicyMapping<P>; 129 130 auto listKey = policiesKey.SubKey(Mapping::KeyName); 131 if (!listKey.has_value()) 132 { 133 return std::nullopt; 134 } 135 136 typename Mapping::value_t items; 137 for (const auto& value : listKey->Values()) 138 { 139 std::optional<Registry::Value> potentialValue = value.Value(); 140 141 if (potentialValue) 142 { 143 auto item = Mapping::ReadAndValidateItem(potentialValue.value()); 144 if (item.has_value()) 145 { 146 items.emplace_back(std::move(item.value())); 147 } 148 else 149 { 150 AICLI_LOG(Core, Warning, << "Failed to read Group Policy list value. Policy [" << Mapping::KeyName << "], Value [" << value.Name() << ']'); 151 } 152 } 153 else 154 { 155 AICLI_LOG(Core, Verbose, << "Group Policy list value not found. Policy [" << Mapping::KeyName << "], Value [" << value.Name() << ']'); 156 } 157 } 158 159 return items; 160 } 161 162 std::optional<SourceFromPolicy> ReadSourceFromRegistryValue(const Registry::Value& item) 163 { 164 auto jsonString = item.TryGetValue<Registry::Value::Type::String>(); 165 if (!jsonString.has_value()) 166 { 167 AICLI_LOG(Core, Warning, << "Registry value is not a string"); 168 return std::nullopt; 169 } 170 171 int stringLength = static_cast<int>(jsonString->length()); 172 Json::Value sourceJson; 173 Json::CharReaderBuilder charReaderBuilder; 174 const std::unique_ptr<Json::CharReader> jsonReader(charReaderBuilder.newCharReader()); 175 Json::String jsonErrors; 176 if (!jsonReader->parse(jsonString->c_str(), jsonString->c_str() + stringLength, &sourceJson, &jsonErrors)) 177 { 178 AICLI_LOG(Core, Warning, << "Registry value does not contain a valid JSON: " << jsonErrors); 179 return std::nullopt; 180 } 181 182 SourceFromPolicy source; 183 184 auto readSourceAttribute = [&](const std::string& name, std::string SourceFromPolicy::* member) 185 { 186 if (sourceJson.isMember(name) && sourceJson[name].isString()) 187 { 188 source.*member = sourceJson[name].asString(); 189 return true; 190 } 191 else 192 { 193 AICLI_LOG(Core, Warning, << "Source JSON does not contain a string value for " << name); 194 return false; 195 } 196 }; 197 198 // All required fields should be read here. 199 bool allRead = readSourceAttribute("Name", &SourceFromPolicy::Name) 200 && readSourceAttribute("Arg", &SourceFromPolicy::Arg) 201 && readSourceAttribute("Type", &SourceFromPolicy::Type) 202 && readSourceAttribute("Data", &SourceFromPolicy::Data) 203 && readSourceAttribute("Identifier", &SourceFromPolicy::Identifier); 204 205 if (!allRead) 206 { 207 return std::nullopt; 208 } 209 210 #ifndef AICLI_DISABLE_TEST_HOOKS 211 // Enable certificate pinning configuration through GP sources for testing 212 const std::string pinningConfigurationName = "CertificatePinning"; 213 if (sourceJson.isMember(pinningConfigurationName)) 214 { 215 source.PinningConfiguration = Certificates::PinningConfiguration(source.Name); 216 if (!source.PinningConfiguration.LoadFrom(sourceJson[pinningConfigurationName])) 217 { 218 return std::nullopt; 219 } 220 } 221 #endif 222 // TrustLevel and Explicit are optional policy fields with default values. 223 const std::string trustLevelName = "TrustLevel"; 224 if (sourceJson.isMember(trustLevelName) && sourceJson[trustLevelName].isArray()) 225 { 226 const Json::Value in = sourceJson[trustLevelName]; 227 std::vector<std::string> result; 228 result.reserve(in.size()); 229 std::transform(in.begin(), in.end(), std::back_inserter(result), [](const auto& e) { return e.asString(); }); 230 source.TrustLevel = result; 231 } 232 233 const std::string explicitName = "Explicit"; 234 if (sourceJson.isMember(explicitName) && sourceJson[explicitName].isBool()) 235 { 236 source.Explicit = sourceJson[explicitName].asBool(); 237 } 238 239 return source; 240 } 241 } 242 243 namespace details 244 { 245 #define POLICY_MAPPING_DEFAULT_READ(_policy_) \ 246 std::optional<typename ValuePolicyMapping<_policy_>::value_t> ValuePolicyMapping<_policy_>::ReadAndValidate(const Registry::Key& policiesKey) \ 247 { \ 248 using Mapping = ValuePolicyMapping<_policy_>; \ 249 return GetRegistryValueData<Mapping::ValueType>(policiesKey, Mapping::ValueName); \ 250 } 251 252 #define POLICY_MAPPING_DEFAULT_LIST_READ(_policy_) \ 253 std::optional<typename ValuePolicyMapping<_policy_>::value_t> ValuePolicyMapping<_policy_>::ReadAndValidate(const Registry::Key& policiesKey) \ 254 { \ 255 return ReadList<_policy_>(policiesKey); \ 256 } 257 258 POLICY_MAPPING_DEFAULT_LIST_READ(ValuePolicy::AdditionalSources); 259 POLICY_MAPPING_DEFAULT_LIST_READ(ValuePolicy::AllowedSources); 260 POLICY_MAPPING_DEFAULT_READ(ValuePolicy::DefaultProxy); 261 262 std::nullopt_t ValuePolicyMapping<ValuePolicy::None>::ReadAndValidate(const Registry::Key&) 263 { 264 return std::nullopt; 265 } 266 267 std::optional<uint32_t> ValuePolicyMapping<ValuePolicy::SourceAutoUpdateIntervalInMinutes>::ReadAndValidate(const Registry::Key& policiesKey) 268 { 269 // This policy used to have another name in the registry. 270 // Try to read first with the current name, and if it's not present 271 // check if the old name is present. 272 using Mapping = ValuePolicyMapping<ValuePolicy::SourceAutoUpdateIntervalInMinutes>; 273 274 auto regValueWithCurrentName = GetRegistryValueObject(policiesKey, Mapping::ValueName); 275 if (regValueWithCurrentName.has_value()) 276 { 277 // We use the current name even if it doesn't have valid data. 278 return GetRegistryValueData<Mapping::ValueType>(regValueWithCurrentName.value(), Mapping::ValueName); 279 } 280 else 281 { 282 return GetRegistryValueData<Mapping::ValueType>(policiesKey, "SourceAutoUpdateIntervalInMinutes"sv); 283 } 284 } 285 286 std::optional<SourceFromPolicy> ValuePolicyMapping<ValuePolicy::AdditionalSources>::ReadAndValidateItem(const Registry::Value& item) 287 { 288 return ReadSourceFromRegistryValue(item); 289 } 290 291 std::optional<SourceFromPolicy> ValuePolicyMapping<ValuePolicy::AllowedSources>::ReadAndValidateItem(const Registry::Value& item) 292 { 293 return ReadSourceFromRegistryValue(item); 294 } 295 } 296 297 TogglePolicy TogglePolicy::GetPolicy(TogglePolicy::Policy policy) 298 { 299 switch (policy) 300 { 301 case TogglePolicy::Policy::WinGet: 302 return TogglePolicy(policy, "EnableAppInstaller"sv, String::PolicyEnableWinGet); 303 case TogglePolicy::Policy::Settings: 304 return TogglePolicy(policy, "EnableSettings"sv, String::PolicyEnableWingetSettings); 305 case TogglePolicy::Policy::ExperimentalFeatures: 306 return TogglePolicy(policy, "EnableExperimentalFeatures"sv, String::PolicyEnableExperimentalFeatures); 307 case TogglePolicy::Policy::LocalManifestFiles: 308 return TogglePolicy(policy, "EnableLocalManifestFiles"sv, String::PolicyEnableLocalManifests); 309 case TogglePolicy::Policy::HashOverride: 310 return TogglePolicy(policy, "EnableHashOverride"sv, String::PolicyEnableHashOverride); 311 case TogglePolicy::Policy::LocalArchiveMalwareScanOverride: 312 return TogglePolicy(policy, "EnableLocalArchiveMalwareScanOverride"sv, String::PolicyEnableLocalArchiveMalwareScanOverride); 313 case TogglePolicy::Policy::DefaultSource: 314 return TogglePolicy(policy, "EnableDefaultSource"sv, String::PolicyEnableDefaultSource); 315 case TogglePolicy::Policy::MSStoreSource: 316 return TogglePolicy(policy, "EnableMicrosoftStoreSource"sv, String::PolicyEnableMSStoreSource); 317 case TogglePolicy::Policy::AdditionalSources: 318 return TogglePolicy(policy, "EnableAdditionalSources"sv, String::PolicyAdditionalSources); 319 case TogglePolicy::Policy::AllowedSources: 320 return TogglePolicy(policy, "EnableAllowedSources"sv, String::PolicyAllowedSources); 321 case TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore: 322 return TogglePolicy(policy, "EnableBypassCertificatePinningForMicrosoftStore"sv, String::PolicyEnableBypassCertificatePinningForMicrosoftStore); 323 case TogglePolicy::Policy::WinGetCommandLineInterfaces: 324 return TogglePolicy(policy, "EnableWindowsPackageManagerCommandLineInterfaces"sv, String::PolicyEnableWindowsPackageManagerCommandLineInterfaces); 325 case TogglePolicy::Policy::Configuration: 326 return TogglePolicy(policy, "EnableWindowsPackageManagerConfiguration"sv, String::PolicyEnableWinGetConfiguration); 327 case TogglePolicy::Policy::ProxyCommandLineOptions: 328 return TogglePolicy(policy, "EnableWindowsPackageManagerProxyCommandLineOptions"sv, String::PolicyEnableProxyCommandLineOptions); 329 case TogglePolicy::Policy::McpServer: 330 return TogglePolicy(policy, "EnableWindowsPackageManagerMcpServer"sv, String::PolicyEnableMcpServer); 331 default: 332 THROW_HR(E_UNEXPECTED); 333 } 334 } 335 336 std::vector<TogglePolicy> TogglePolicy::GetAllPolicies() 337 { 338 using Toggle_t = std::underlying_type_t<TogglePolicy::Policy>; 339 340 std::vector<TogglePolicy> result; 341 342 // Skip "None" 343 for (Toggle_t i = 1 + static_cast<Toggle_t>(TogglePolicy::Policy::None); i < static_cast<Toggle_t>(TogglePolicy::Policy::Max); ++i) 344 { 345 result.emplace_back(GetPolicy(static_cast<Policy>(i))); 346 } 347 348 return result; 349 } 350 351 std::string SourceFromPolicy::ToJsonString() const 352 { 353 Json::Value json{ Json::ValueType::objectValue }; 354 json["Name"] = Name; 355 json["Type"] = Type; 356 json["Arg"] = Arg; 357 json["Data"] = Data; 358 json["Identifier"] = Identifier; 359 json["Explicit"] = Explicit; 360 361 // Trust level is represented as an array of trust level strings since there can be multiple flags set. 362 int trustLevelLength = static_cast<int>(TrustLevel.size()); 363 for (int i = 0; i < trustLevelLength; ++i) 364 { 365 json["TrustLevel"][i] = TrustLevel[i]; 366 } 367 368 Json::StreamWriterBuilder writerBuilder; 369 writerBuilder.settings_["indentation"] = ""; 370 return Json::writeString(writerBuilder, json); 371 } 372 373 GroupPolicy::GroupPolicy(const Registry::Key& key) 374 { 375 ValidateAllValuePolicies(key, m_values, std::make_index_sequence<static_cast<size_t>(ValuePolicy::Max)>()); 376 377 using Toggle_t = std::underlying_type_t<TogglePolicy::Policy>; 378 for (Toggle_t i = static_cast<Toggle_t>(TogglePolicy::Policy::None); i < static_cast<Toggle_t>(TogglePolicy::Policy::Max); ++i) 379 { 380 auto policy = static_cast<TogglePolicy::Policy>(i); 381 m_toggles[policy] = GetStateInternal(key, policy); 382 } 383 } 384 385 PolicyState GroupPolicy::GetState(TogglePolicy::Policy policy) const 386 { 387 auto itr = m_toggles.find(policy); 388 if (itr == m_toggles.end()) 389 { 390 return PolicyState::NotConfigured; 391 } 392 393 return itr->second; 394 } 395 396 bool GroupPolicy::IsEnabled(TogglePolicy::Policy policy) const 397 { 398 if (policy == TogglePolicy::Policy::None) 399 { 400 return true; 401 } 402 403 PolicyState state = GetState(policy); 404 if (state == PolicyState::NotConfigured) 405 { 406 return TogglePolicy::GetPolicy(policy).DefaultIsEnabled(); 407 } 408 409 return state == PolicyState::Enabled; 410 } 411 412 GroupPolicy const& GroupPolicy::Instance() 413 { 414 return InstanceInternal(); 415 } 416 417 #ifndef AICLI_DISABLE_TEST_HOOKS 418 void GroupPolicy::OverrideInstance(GroupPolicy* overridePolicy) 419 { 420 InstanceInternal(overridePolicy); 421 } 422 423 void GroupPolicy::ResetInstance() 424 { 425 InstanceInternal(nullptr); 426 } 427 #endif 428 }