AdminSettings.cpp (17026B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "AppInstallerLogging.h" 5 #include "AppInstallerStrings.h" 6 #include "winget/Settings.h" 7 #include "winget/AdminSettings.h" 8 #include "winget/GroupPolicy.h" 9 #include "winget/Yaml.h" 10 11 namespace AppInstaller::Settings 12 { 13 using namespace std::string_view_literals; 14 using namespace Utility::literals; 15 16 namespace 17 { 18 constexpr Utility::LocIndView s_AdminSettingsYaml_LocalManifestFiles = "LocalManifestFiles"_liv; 19 constexpr Utility::LocIndView s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore = "BypassCertificatePinningForMicrosoftStore"_liv; 20 constexpr Utility::LocIndView s_AdminSettingsYaml_InstallerHashOverride = "InstallerHashOverride"_liv; 21 constexpr Utility::LocIndView s_AdminSettingsYaml_LocalArchiveMalwareScanOverride = "LocalArchiveMalwareScanOverride"_liv; 22 constexpr Utility::LocIndView s_AdminSettingsYaml_ProxyCommandLineOptions = "ProxyCommandLineOptions"_liv; 23 24 constexpr Utility::LocIndView s_AdminSettingsYaml_DefaultProxy = "DefaultProxy"_liv; 25 26 // Attempts to read a single scalar value from the node. 27 template<typename Value> 28 bool TryReadScalar(const YAML::Node& rootNode, std::string_view name, Value& value) 29 { 30 YAML::Node valueNode = rootNode[std::string{ name }]; 31 32 if (!valueNode || !valueNode.IsScalar()) 33 { 34 AICLI_LOG(Core, Verbose, << "Admin setting '" << name << "' was not found or did not contain the expected format"); 35 return false; 36 } 37 38 value = valueNode.as<Value>(); 39 return true; 40 } 41 42 struct AdminSettingValues 43 { 44 bool LocalManifestFiles = false; 45 bool BypassCertificatePinningForMicrosoftStore = false; 46 bool InstallerHashOverride = false; 47 bool LocalArchiveMalwareScanOverride = false; 48 bool ProxyCommandLineOptions = false; 49 50 std::optional<std::string> DefaultProxy; 51 }; 52 53 struct AdminSettingsInternal 54 { 55 AdminSettingsInternal(); 56 57 void SetAdminSetting(BoolAdminSetting setting, bool enabled); 58 void SetAdminSetting(StringAdminSetting setting, const std::optional<std::string>& value); 59 60 bool GetAdminSettingValue(BoolAdminSetting setting) const; 61 std::optional<std::string> GetAdminSettingValue(StringAdminSetting setting) const; 62 63 void Reset(); 64 65 private: 66 void LoadAdminSettings(); 67 [[nodiscard]] bool SaveAdminSettings(); 68 69 // Sets the value of an admin setting using the given function and then saves the changes. 70 // Encapsulates the retry and reload logic. 71 // Stops if the value cannot be set, as indicated by the return value of setValue() 72 void SetAdminSettingAndSave(std::function<bool()> setValue); 73 74 Stream m_settingStream; 75 AdminSettingValues m_settingValues; 76 }; 77 78 AdminSettingsInternal::AdminSettingsInternal() : m_settingStream(Stream::AdminSettings) 79 { 80 LoadAdminSettings(); 81 } 82 83 void AdminSettingsInternal::SetAdminSettingAndSave(std::function<bool()> setValue) 84 { 85 for (size_t i = 0; i < 10; ++i) 86 { 87 if (!setValue()) 88 { 89 return; 90 } 91 92 if (SaveAdminSettings()) 93 { 94 return; 95 } 96 97 // We need to reload the settings as they have changed 98 LoadAdminSettings(); 99 } 100 101 THROW_HR_MSG(E_UNEXPECTED, "Too many attempts at SaveAdminSettings"); 102 } 103 104 void AdminSettingsInternal::SetAdminSetting(BoolAdminSetting setting, bool enabled) 105 { 106 SetAdminSettingAndSave([&]() 107 { 108 switch (setting) 109 { 110 case BoolAdminSetting::LocalManifestFiles: 111 m_settingValues.LocalManifestFiles = enabled; 112 return true; 113 case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: 114 m_settingValues.BypassCertificatePinningForMicrosoftStore = enabled; 115 return true; 116 case BoolAdminSetting::InstallerHashOverride: 117 m_settingValues.InstallerHashOverride = enabled; 118 return true; 119 case BoolAdminSetting::LocalArchiveMalwareScanOverride: 120 m_settingValues.LocalArchiveMalwareScanOverride = enabled; 121 return true; 122 case BoolAdminSetting::ProxyCommandLineOptions: 123 m_settingValues.ProxyCommandLineOptions = enabled; 124 return true; 125 default: 126 return false; 127 } 128 }); 129 } 130 131 void AdminSettingsInternal::SetAdminSetting(StringAdminSetting setting, const std::optional<std::string>& value) 132 { 133 SetAdminSettingAndSave([&]() 134 { 135 switch (setting) 136 { 137 case StringAdminSetting::DefaultProxy: 138 m_settingValues.DefaultProxy = value; 139 return true; 140 default: 141 return false; 142 } 143 }); 144 } 145 146 bool AdminSettingsInternal::GetAdminSettingValue(BoolAdminSetting setting) const 147 { 148 switch (setting) 149 { 150 case BoolAdminSetting::LocalManifestFiles: 151 return m_settingValues.LocalManifestFiles; 152 case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: 153 return m_settingValues.BypassCertificatePinningForMicrosoftStore; 154 case BoolAdminSetting::InstallerHashOverride: 155 return m_settingValues.InstallerHashOverride; 156 case BoolAdminSetting::LocalArchiveMalwareScanOverride: 157 return m_settingValues.LocalArchiveMalwareScanOverride; 158 case BoolAdminSetting::ProxyCommandLineOptions: 159 return m_settingValues.ProxyCommandLineOptions; 160 default: 161 return false; 162 } 163 } 164 165 std::optional<std::string> AdminSettingsInternal::GetAdminSettingValue(StringAdminSetting setting) const 166 { 167 switch (setting) 168 { 169 case StringAdminSetting::DefaultProxy: 170 return m_settingValues.DefaultProxy; 171 default: 172 return std::nullopt; 173 } 174 } 175 176 void AdminSettingsInternal::Reset() 177 { 178 m_settingStream.Remove(); 179 } 180 181 void AdminSettingsInternal::LoadAdminSettings() 182 { 183 auto stream = m_settingStream.Get(); 184 if (!stream) 185 { 186 AICLI_LOG(Core, Verbose, << "Admin settings was not found"); 187 return; 188 } 189 190 std::string adminSettingsYaml = Utility::ReadEntireStream(*stream); 191 192 YAML::Node document; 193 try 194 { 195 document = YAML::Load(adminSettingsYaml); 196 } 197 catch (const std::exception& e) 198 { 199 AICLI_LOG(YAML, Error, << "Admin settings contained invalid YAML (" << e.what() << ")"); 200 return; 201 } 202 203 if (document.IsNull()) 204 { 205 AICLI_LOG(Core, Info, << "Admin settings is empty"); 206 return; 207 } 208 209 if (!document.IsMap()) 210 { 211 AICLI_LOG(Core, Error, << "Admin settings did not contain the expected format"); 212 return; 213 } 214 215 TryReadScalar<bool>(document, s_AdminSettingsYaml_LocalManifestFiles, m_settingValues.LocalManifestFiles); 216 TryReadScalar<bool>(document, s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore, m_settingValues.BypassCertificatePinningForMicrosoftStore); 217 TryReadScalar<bool>(document, s_AdminSettingsYaml_InstallerHashOverride, m_settingValues.InstallerHashOverride); 218 TryReadScalar<bool>(document, s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, m_settingValues.LocalArchiveMalwareScanOverride); 219 TryReadScalar<bool>(document, s_AdminSettingsYaml_ProxyCommandLineOptions, m_settingValues.ProxyCommandLineOptions); 220 221 std::string defaultProxy; 222 if (TryReadScalar<std::string>(document, s_AdminSettingsYaml_DefaultProxy, defaultProxy)) 223 { 224 m_settingValues.DefaultProxy.emplace(std::move(defaultProxy)); 225 } 226 } 227 228 bool AdminSettingsInternal::SaveAdminSettings() 229 { 230 YAML::Emitter out; 231 out << YAML::BeginMap; 232 out << YAML::Key << s_AdminSettingsYaml_LocalManifestFiles << YAML::Value << m_settingValues.LocalManifestFiles; 233 out << YAML::Key << s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore << YAML::Value << m_settingValues.BypassCertificatePinningForMicrosoftStore; 234 out << YAML::Key << s_AdminSettingsYaml_InstallerHashOverride << YAML::Value << m_settingValues.InstallerHashOverride; 235 out << YAML::Key << s_AdminSettingsYaml_LocalArchiveMalwareScanOverride << YAML::Value << m_settingValues.LocalArchiveMalwareScanOverride; 236 out << YAML::Key << s_AdminSettingsYaml_ProxyCommandLineOptions << YAML::Value << m_settingValues.ProxyCommandLineOptions; 237 238 if (m_settingValues.DefaultProxy) 239 { 240 out << YAML::Key << s_AdminSettingsYaml_DefaultProxy << YAML::Value << m_settingValues.DefaultProxy.value(); 241 } 242 243 out << YAML::EndMap; 244 245 return m_settingStream.Set(out.str()); 246 } 247 248 auto GetPolicyStateForSetting(BoolAdminSetting setting) 249 { 250 auto policy = GetAdminSettingPolicy(setting); 251 return GroupPolicies().GetState(policy); 252 } 253 254 std::optional<std::reference_wrapper<const std::string>> GetPolicyStateForSetting(StringAdminSetting setting) 255 { 256 switch (setting) 257 { 258 case AppInstaller::Settings::StringAdminSetting::DefaultProxy: 259 return GroupPolicies().GetValueRef<ValuePolicy::DefaultProxy>(); 260 default: 261 return std::nullopt; 262 } 263 } 264 } 265 266 BoolAdminSetting StringToBoolAdminSetting(std::string_view in) 267 { 268 BoolAdminSetting result = BoolAdminSetting::Unknown; 269 270 if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_LocalManifestFiles, in)) 271 { 272 result = BoolAdminSetting::LocalManifestFiles; 273 } 274 else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore, in)) 275 { 276 result = BoolAdminSetting::BypassCertificatePinningForMicrosoftStore; 277 } 278 else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_InstallerHashOverride, in)) 279 { 280 result = BoolAdminSetting::InstallerHashOverride; 281 } 282 else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_LocalArchiveMalwareScanOverride, in)) 283 { 284 result = BoolAdminSetting::LocalArchiveMalwareScanOverride; 285 } 286 else if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_ProxyCommandLineOptions, in)) 287 { 288 result = BoolAdminSetting::ProxyCommandLineOptions; 289 } 290 291 return result; 292 } 293 294 StringAdminSetting StringToStringAdminSetting(std::string_view in) 295 { 296 StringAdminSetting result = StringAdminSetting::Unknown; 297 298 if (Utility::CaseInsensitiveEquals(s_AdminSettingsYaml_DefaultProxy, in)) 299 { 300 result = StringAdminSetting::DefaultProxy; 301 } 302 303 return result; 304 } 305 306 Utility::LocIndView AdminSettingToString(BoolAdminSetting setting) 307 { 308 switch (setting) 309 { 310 case BoolAdminSetting::LocalManifestFiles: 311 return s_AdminSettingsYaml_LocalManifestFiles; 312 case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: 313 return s_AdminSettingsYaml_BypassCertificatePinningForMicrosoftStore; 314 case BoolAdminSetting::InstallerHashOverride: 315 return s_AdminSettingsYaml_InstallerHashOverride; 316 case BoolAdminSetting::LocalArchiveMalwareScanOverride: 317 return s_AdminSettingsYaml_LocalArchiveMalwareScanOverride; 318 case BoolAdminSetting::ProxyCommandLineOptions: 319 return s_AdminSettingsYaml_ProxyCommandLineOptions; 320 default: 321 return "Unknown"_liv; 322 } 323 } 324 325 Utility::LocIndView AdminSettingToString(StringAdminSetting setting) 326 { 327 switch (setting) 328 { 329 case StringAdminSetting::DefaultProxy: 330 return s_AdminSettingsYaml_DefaultProxy; 331 default: 332 return "Unknown"_liv; 333 } 334 } 335 336 TogglePolicy::Policy GetAdminSettingPolicy(BoolAdminSetting setting) 337 { 338 switch (setting) 339 { 340 case BoolAdminSetting::LocalManifestFiles: 341 return TogglePolicy::Policy::LocalManifestFiles; 342 case BoolAdminSetting::BypassCertificatePinningForMicrosoftStore: 343 return TogglePolicy::Policy::BypassCertificatePinningForMicrosoftStore; 344 case BoolAdminSetting::InstallerHashOverride: 345 return TogglePolicy::Policy::HashOverride; 346 case BoolAdminSetting::LocalArchiveMalwareScanOverride: 347 return TogglePolicy::Policy::LocalArchiveMalwareScanOverride; 348 case BoolAdminSetting::ProxyCommandLineOptions: 349 return TogglePolicy::Policy::ProxyCommandLineOptions; 350 default: 351 return TogglePolicy::Policy::None; 352 } 353 } 354 355 bool EnableAdminSetting(BoolAdminSetting setting) 356 { 357 if (GetPolicyStateForSetting(setting) == PolicyState::Disabled) 358 { 359 return false; 360 } 361 362 AdminSettingsInternal adminSettingsInternal; 363 adminSettingsInternal.SetAdminSetting(setting, true); 364 return true; 365 } 366 367 bool DisableAdminSetting(BoolAdminSetting setting) 368 { 369 if (GetPolicyStateForSetting(setting) == PolicyState::Enabled) 370 { 371 return false; 372 } 373 374 AdminSettingsInternal adminSettingsInternal; 375 adminSettingsInternal.SetAdminSetting(setting, false); 376 return true; 377 } 378 379 bool IsAdminSettingEnabled(BoolAdminSetting setting) 380 { 381 // Check for a policy that overrides this setting. 382 auto policyState = GetPolicyStateForSetting(setting); 383 if (policyState != PolicyState::NotConfigured) 384 { 385 return policyState == PolicyState::Enabled; 386 } 387 388 AdminSettingsInternal adminSettingsInternal; 389 return adminSettingsInternal.GetAdminSettingValue(setting); 390 } 391 392 bool SetAdminSetting(StringAdminSetting setting, std::string_view value) 393 { 394 if (GetPolicyStateForSetting(setting)) 395 { 396 return false; 397 } 398 399 AdminSettingsInternal adminSettingsInternal; 400 adminSettingsInternal.SetAdminSetting(setting, std::string{ value }); 401 return true; 402 } 403 404 bool ResetAdminSetting(StringAdminSetting setting) 405 { 406 if (GetPolicyStateForSetting(setting)) 407 { 408 return false; 409 } 410 411 AdminSettingsInternal adminSettingsInternal; 412 adminSettingsInternal.SetAdminSetting(setting, std::nullopt); 413 return true; 414 } 415 416 void ResetAllAdminSettings() 417 { 418 AdminSettingsInternal{}.Reset(); 419 } 420 421 std::optional<std::string> GetAdminSetting(StringAdminSetting setting) 422 { 423 // Check for a policy that overrides this setting. 424 auto policyState = GetPolicyStateForSetting(setting); 425 if (policyState) 426 { 427 return policyState.value(); 428 } 429 430 AdminSettingsInternal adminSettingsInternal; 431 return adminSettingsInternal.GetAdminSettingValue(setting); 432 } 433 434 std::vector<BoolAdminSetting> GetAllBoolAdminSettings() 435 { 436 return GetAllSequentialEnumValues(BoolAdminSetting::Unknown); 437 } 438 439 std::vector<StringAdminSetting> GetAllStringAdminSettings() 440 { 441 return GetAllSequentialEnumValues(StringAdminSetting::Unknown); 442 } 443 444 }