SourcePolicy.cpp (9639B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "SourcePolicy.h" 5 #include "Microsoft/PreIndexedPackageSourceFactory.h" 6 #include "Rest/RestSourceFactory.h" 7 8 using namespace AppInstaller::Settings; 9 10 11 namespace AppInstaller::Repository 12 { 13 namespace 14 { 15 // Checks whether a default source is enabled with the current settings. 16 // onlyExplicit determines whether we consider the not-configured state to be enabled or not. 17 bool IsDefaultSourceEnabled(WellKnownSource sourceToLog, ExperimentalFeature::Feature feature, bool onlyExplicit, TogglePolicy::Policy policy) 18 { 19 if (!ExperimentalFeature::IsEnabled(feature)) 20 { 21 // No need to log here 22 return false; 23 } 24 25 if (onlyExplicit) 26 { 27 // No need to log here 28 return GroupPolicies().GetState(policy) == PolicyState::Enabled; 29 } 30 31 if (!GroupPolicies().IsEnabled(policy)) 32 { 33 AICLI_LOG(Repo, Info, << "The default source " << GetWellKnownSourceName(sourceToLog) << " is disabled due to Group Policy"); 34 return false; 35 } 36 37 return true; 38 } 39 40 template<ValuePolicy P> 41 std::optional<SourceFromPolicy> FindSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) 42 { 43 auto sourcesOpt = GroupPolicies().GetValueRef<P>(); 44 if (!sourcesOpt.has_value()) 45 { 46 return std::nullopt; 47 } 48 49 const auto& sources = sourcesOpt->get(); 50 auto source = std::find_if( 51 sources.begin(), 52 sources.end(), 53 [&](const SourceFromPolicy& policySource) 54 { 55 return Utility::ICUCaseInsensitiveEquals(name, policySource.Name) && Utility::ICUCaseInsensitiveEquals(type, policySource.Type) && arg == policySource.Arg; 56 }); 57 58 if (source == sources.end()) 59 { 60 return std::nullopt; 61 } 62 63 return *source; 64 } 65 66 template<ValuePolicy P> 67 bool IsSourceInPolicy(std::string_view name, std::string_view type, std::string_view arg) 68 { 69 return FindSourceInPolicy<P>(name, type, arg).has_value(); 70 } 71 } 72 73 // Checks whether the Group Policy allows this user source. 74 // If it does it returns None, otherwise it returns which policy is blocking it. 75 // Note that this applies to user sources that are being added as well as user sources 76 // that already existed when the Group Policy came into effect. 77 TogglePolicy::Policy GetPolicyBlockingUserSource(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) 78 { 79 // Reasons for not allowing: 80 // 1. The source is a tombstone for default source that is explicitly enabled 81 // 2. The source is a default source that is disabled 82 // 3. The source has the same name as a default source that is explicitly enabled (to prevent shadowing) 83 // 4. Allowed sources are disabled, blocking all user sources 84 // 5. There is an explicit list of allowed sources and this source is not in it 85 // 86 // We don't need to check sources added by policy as those have higher priority. 87 // 88 // Use the name and arg to match sources as we don't have the identifier before adding. 89 90 // Case 1: 91 // The source is a tombstone and we need the policy to be explicitly enabled. 92 if (isTombstone) 93 { 94 if (name == GetWellKnownSourceName(WellKnownSource::WinGet) && IsWellKnownSourceEnabled(WellKnownSource::WinGet, true)) 95 { 96 return TogglePolicy::Policy::DefaultSource; 97 } 98 99 if (name == GetWellKnownSourceName(WellKnownSource::MicrosoftStore) && IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore, true)) 100 { 101 return TogglePolicy::Policy::MSStoreSource; 102 } 103 104 // Any other tombstone is allowed 105 return TogglePolicy::Policy::None; 106 } 107 108 // Case 2: 109 // - The source is not a tombstone and we don't need the policy to be explicitly enabled. 110 // - Check only against the source argument and type as the user source may have a different name. 111 // - Do a case-insensitive check as the domain portion of the URL is case-insensitive, 112 // and we don't need case sensitivity for the rest as we control the domain. 113 if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::WinGet)) && 114 Utility::CaseInsensitiveEquals(type, Microsoft::PreIndexedPackageSourceFactory::Type())) 115 { 116 return IsWellKnownSourceEnabled(WellKnownSource::WinGet) ? TogglePolicy::Policy::None : TogglePolicy::Policy::DefaultSource; 117 } 118 119 if (Utility::CaseInsensitiveEquals(arg, GetWellKnownSourceArg(WellKnownSource::MicrosoftStore)) && 120 Utility::CaseInsensitiveEquals(type, Rest::RestSourceFactory::Type())) 121 { 122 return IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore) ? TogglePolicy::Policy::None : TogglePolicy::Policy::MSStoreSource; 123 } 124 125 // Case 3: 126 // If the source has the same name as a default source, it is shadowing with a different argument 127 // (as it didn't match above). We only care if Group Policy requires the default source. 128 if (name == GetWellKnownSourceName(WellKnownSource::WinGet) && IsWellKnownSourceEnabled(WellKnownSource::WinGet, true)) 129 { 130 AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows the default source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); 131 return TogglePolicy::Policy::DefaultSource; 132 } 133 134 if (name == GetWellKnownSourceName(WellKnownSource::MicrosoftStore) && IsWellKnownSourceEnabled(WellKnownSource::MicrosoftStore, true)) 135 { 136 AICLI_LOG(Repo, Warning, << "User source is not allowed as it shadows a default MS Store source. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); 137 return TogglePolicy::Policy::MSStoreSource; 138 } 139 140 // Case 4: 141 // The guard in the source add command should already block adding. 142 // This check drops existing user sources. 143 auto allowedSourcesPolicy = GroupPolicies().GetState(TogglePolicy::Policy::AllowedSources); 144 if (allowedSourcesPolicy == PolicyState::Disabled) 145 { 146 AICLI_LOG(Repo, Warning, << "User sources are disabled by Group Policy"); 147 return TogglePolicy::Policy::AllowedSources; 148 } 149 150 // Case 5: 151 if (allowedSourcesPolicy == PolicyState::Enabled) 152 { 153 if (!IsSourceInPolicy<ValuePolicy::AllowedSources>(name, type, arg)) 154 { 155 AICLI_LOG(Repo, Warning, << "Source is not in the Group Policy allowed list. Name [" << name << "]. Arg [" << arg << "] Type [" << type << ']'); 156 return TogglePolicy::Policy::AllowedSources; 157 } 158 } 159 160 return TogglePolicy::Policy::None; 161 } 162 163 bool IsUserSourceAllowedByPolicy(std::string_view name, std::string_view type, std::string_view arg, bool isTombstone) 164 { 165 return GetPolicyBlockingUserSource(name, type, arg, isTombstone) == TogglePolicy::Policy::None; 166 } 167 168 bool IsWellKnownSourceEnabled(WellKnownSource source, bool onlyExplicit) 169 { 170 switch (source) 171 { 172 case AppInstaller::Repository::WellKnownSource::WinGet: 173 return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::DefaultSource); 174 case AppInstaller::Repository::WellKnownSource::MicrosoftStore: 175 return IsDefaultSourceEnabled(source, ExperimentalFeature::Feature::None, onlyExplicit, TogglePolicy::Policy::MSStoreSource); 176 case AppInstaller::Repository::WellKnownSource::DesktopFrameworks: 177 // No corresponding policy available for this source. 178 return true; 179 } 180 181 return false; 182 } 183 184 void EnsureSourceIsRemovable(const SourceDetailsInternal& source) 185 { 186 // Block removing sources added by Group Policy 187 if (source.Origin == SourceOrigin::GroupPolicy) 188 { 189 AICLI_LOG(Repo, Error, << "Cannot remove source added by Group Policy"); 190 throw GroupPolicyException(TogglePolicy::Policy::AdditionalSources); 191 } 192 193 // Block removing default sources required by Group Policy. 194 if (source.Origin == SourceOrigin::Default) 195 { 196 if (GroupPolicies().GetState(TogglePolicy::Policy::DefaultSource) == PolicyState::Enabled && 197 source.Identifier == GetWellKnownSourceIdentifier(WellKnownSource::WinGet)) 198 { 199 throw GroupPolicyException(TogglePolicy::Policy::DefaultSource); 200 } 201 202 if (GroupPolicies().GetState(TogglePolicy::Policy::MSStoreSource) == PolicyState::Enabled && 203 source.Identifier == GetWellKnownSourceIdentifier(WellKnownSource::MicrosoftStore)) 204 { 205 throw GroupPolicyException(TogglePolicy::Policy::MSStoreSource); 206 } 207 } 208 } 209 }