winget-cli

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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 }