/* * Copyright (c) 2024 ETH Zürich, IT Services * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Win32; using SafeExamBrowser.Lockdown.Contracts; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations { [Serializable] internal abstract class RegistryConfiguration : FeatureConfiguration { private IList itemsToDelete; private IList itemsToRestore; protected abstract IEnumerable Items { get; } protected abstract RegistryKey RootKey { get; } public RegistryConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { itemsToDelete = new List(); itemsToRestore = new List(); } protected abstract bool IsHiveAvailable(RegistryDataItem item); public override bool DisableFeature() { var success = true; foreach (var item in Items) { success &= TrySet(new RegistryDataItem { Key = item.Key, Value = item.Value, Data = item.Disabled }); } if (success) { logger.Info("Successfully disabled feature."); } else { logger.Warn("Failed to disable feature!"); } return success; } public override bool EnableFeature() { var success = true; foreach (var item in Items) { success &= TrySet(new RegistryDataItem { Key = item.Key, Value = item.Value, Data = item.Enabled }); } if (success) { logger.Info("Successfully enabled feature."); } else { logger.Warn("Failed to enable feature!"); } return success; } public override void Initialize() { foreach (var item in Items) { var original = ReadItem(item.Key, item.Value); if (original.Data == null) { itemsToDelete.Add(original); } else { itemsToRestore.Add(original); } } } public override FeatureConfigurationStatus GetStatus() { var status = FeatureConfigurationStatus.Undefined; foreach (var item in Items) { var current = ReadItem(item.Key, item.Value); if (current.Data?.Equals(item.Disabled) == true && status != FeatureConfigurationStatus.Enabled) { status = FeatureConfigurationStatus.Disabled; } else if (current.Data?.Equals(item.Enabled) == true && status != FeatureConfigurationStatus.Disabled) { status = FeatureConfigurationStatus.Enabled; } else { status = FeatureConfigurationStatus.Undefined; break; } } return status; } public override bool Restore() { foreach (var item in new List(itemsToDelete)) { if (TryDelete(item)) { itemsToDelete.Remove(item); } } foreach (var item in new List(itemsToRestore)) { if (TrySet(item)) { itemsToRestore.Remove(item); } } var success = !itemsToDelete.Any() && !itemsToRestore.Any(); if (success) { logger.Info("Successfully restored feature."); } else { logger.Warn("Failed to restore feature!"); } return success; } protected bool DeleteConfiguration() { var success = true; foreach (var item in Items) { success &= TryDelete(new RegistryDataItem { Key = item.Key, Value = item.Value }); } if (success) { logger.Info("Successfully deleted feature configuration."); } else { logger.Warn("Failed to delete feature configuration!"); } return success; } private RegistryDataItem ReadItem(string key, string value) { var data = Registry.GetValue(key, value, null); var original = new RegistryDataItem { Key = key, Value = value, Data = data }; return original; } private bool TryDelete(RegistryDataItem item) { var success = false; try { if (IsHiveAvailable(item)) { var keyWithoutRoot = item.Key.Substring(item.Key.IndexOf('\\') + 1); using (var key = RootKey.OpenSubKey(keyWithoutRoot, true)) { if (key.GetValue(item.Value) != null) { key.DeleteValue(item.Value); logger.Debug($"Successfully deleted registry item {item}."); } else { logger.Debug($"No need to delete registry item {item} as it does not exist."); } success = true; } } else { logger.Warn($"Cannot delete item {item} as its registry hive is not available!"); } } catch (Exception e) { logger.Error($"Failed to delete registry item {item}!", e); } return success; } private bool TrySet(RegistryDataItem item) { var success = false; try { if (IsHiveAvailable(item)) { Registry.SetValue(item.Key, item.Value, item.Data); logger.Debug($"Successfully set registry item {item}."); success = true; } else { logger.Warn($"Cannot set item {item} as its registry hive is not available!"); } } catch (Exception e) { logger.Error($"Failed to set registry item {item}!", e); } return success; } } }