Restore SEBPatch

This commit is contained in:
2025-06-01 11:44:20 +02:00
commit 8c656e3137
1297 changed files with 142172 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
/*
* 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.Runtime.Serialization;
using SafeExamBrowser.Lockdown.Contracts;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
[Serializable]
internal abstract class FeatureConfiguration : IFeatureConfiguration
{
[NonSerialized]
protected ILogger logger;
public Guid Id { get; }
public Guid GroupId { get; }
public FeatureConfiguration(Guid groupId, ILogger logger)
{
this.GroupId = groupId;
this.Id = Guid.NewGuid();
this.logger = logger;
}
public abstract bool DisableFeature();
public abstract bool EnableFeature();
public abstract FeatureConfigurationStatus GetStatus();
public abstract void Initialize();
public abstract bool Reset();
public abstract bool Restore();
public override string ToString()
{
return $"{GetType().Name} ({Id})";
}
[OnDeserialized]
private void OnDeserializedMethod(StreamingContext context)
{
logger = (context.Context as IModuleLogger).CloneFor(GetType().Name);
}
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
/// <summary>
/// Controls whether the ease of access option is available on the Security / Login Screen of the operating system. See also
/// https://learn.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-embedded-embeddedlogon-brandingneutral.
/// </summary>
[Serializable]
internal class EaseOfAccessConfiguration : MachineHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new[]
{
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Embedded\EmbeddedLogon", "BrandingNeutral", 8, 0),
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\Utilman.exe", "Debugger", "SebDummy.exe", "")
};
public EaseOfAccessConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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 Microsoft.Win32;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
[Serializable]
internal abstract class MachineHiveConfiguration : RegistryConfiguration
{
protected override RegistryKey RootKey => Registry.LocalMachine;
public MachineHiveConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
protected override bool IsHiveAvailable(RegistryDataItem item)
{
return true;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
[Serializable]
internal class MachinePowerOptionsConfiguration : MachineHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "NoClose", 1, 0)
};
public MachinePowerOptionsConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
[Serializable]
internal class NetworkOptionsConfiguration : MachineHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\System", "DontDisplayNetworkSelectionUI", 1, 0)
};
public NetworkOptionsConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
/// <summary>
/// Specifies whether Remote Desktop connections are enabled.
///
/// See https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-terminalservices-localsessionmanager-fdenytsconnections:
/// 0 = Specifies that remote desktop connections are enabled.
/// 1 = Specifies that remote desktop connections are denied. This is the default value.
/// </summary>
[Serializable]
internal class RemoteConnectionConfiguration : MachineHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new[]
{
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server", "fDenyTSConnections", 1, 0)
};
public RemoteConnectionConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return DisableFeature();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive
{
[Serializable]
internal class SwitchUserConfiguration : MachineHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem(@"HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\System", "HideFastUserSwitching", 1, 0)
};
public SwitchUserConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,245 @@
/*
* 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<RegistryDataItem> itemsToDelete;
private IList<RegistryDataItem> itemsToRestore;
protected abstract IEnumerable<RegistryConfigurationItem> Items { get; }
protected abstract RegistryKey RootKey { get; }
public RegistryConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
itemsToDelete = new List<RegistryDataItem>();
itemsToRestore = new List<RegistryDataItem>();
}
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<RegistryDataItem>(itemsToDelete))
{
if (TryDelete(item))
{
itemsToDelete.Remove(item);
}
}
foreach (var item in new List<RegistryDataItem>(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;
}
}
}

View File

@@ -0,0 +1,26 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations
{
internal class RegistryConfigurationItem
{
internal object Disabled { get; }
internal object Enabled { get; }
internal string Key { get; }
internal string Value { get; }
internal RegistryConfigurationItem(string key, string value, object disabled, object enabled)
{
Key = key;
Value = value;
Disabled = disabled;
Enabled = enabled;
}
}
}

View File

@@ -0,0 +1,25 @@
/*
* 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;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations
{
[Serializable]
internal class RegistryDataItem
{
internal object Data { get; set; }
internal string Key { get; set; }
internal string Value { get; set; }
public override string ToString()
{
return $@"'{Key}\{Value}'{(Data != null ? $" => '{Data}'" : "")}";
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class ChangePasswordConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\System", "DisableChangePassword", 1, 0)
};
public ChangePasswordConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
/// <summary>
/// IMPORTANT: This registry configuration only has an effect after Chrome is restarted!
///
/// See https://www.chromium.org/administrators/policy-list-3#DefaultNotificationsSetting:
/// • 1 = Allow sites to show desktop notifications.
/// • 2 = Do not allow any site to show desktop notifications.
/// • 3 = Ask every time a site wants to show desktop notifications.
/// </summary>
[Serializable]
internal class ChromeNotificationConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Policies\Google\Chrome", "DefaultNotificationsSetting", 2, 1)
};
public ChromeNotificationConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class FindPrinterConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new[]
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "NoAddPrinter", 1, 0)
};
public FindPrinterConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class LockWorkstationConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\System", "DisableLockWorkstation", 1, 0)
};
public LockWorkstationConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class SignoutConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "NoLogoff", 1, 0)
};
public SignoutConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class TaskManagerConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\System", "DisableTaskMgr", 1, 0)
};
public TaskManagerConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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 Microsoft.Win32;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal abstract class UserHiveConfiguration : RegistryConfiguration
{
protected string SID { get; }
protected string UserName { get; }
protected override RegistryKey RootKey => Registry.Users;
public UserHiveConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger)
{
SID = sid ?? throw new ArgumentNullException(nameof(sid));
UserName = userName ?? throw new ArgumentNullException(nameof(userName));
}
public override string ToString()
{
return $"{GetType().Name} ({Id}) for user '{UserName}'";
}
protected override bool IsHiveAvailable(RegistryDataItem item)
{
var isAvailable = false;
try
{
isAvailable = Registry.Users.OpenSubKey(SID) != null;
}
catch (Exception e)
{
logger.Error($"Failed to check availability of registry hive for item {item}!", e);
}
return isAvailable;
}
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class UserPowerOptionsConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new[]
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer", "NoClose", 1, 0)
};
public UserPowerOptionsConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return DeleteConfiguration();
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive
{
[Serializable]
internal class VmwareOverlayConfiguration : UserHiveConfiguration
{
protected override IEnumerable<RegistryConfigurationItem> Items => new []
{
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\VMware, Inc.\VMware VDM\Client", "EnableShade", 0, 1),
new RegistryConfigurationItem($@"HKEY_USERS\{SID}\Software\Policies\VMware, Inc.\VMware VDM\Client", "EnableShade", "False", "True")
};
public VmwareOverlayConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName)
{
}
public override bool Reset()
{
return EnableFeature();
}
}
}

View File

@@ -0,0 +1,319 @@
/*
* 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 System.ServiceProcess;
using SafeExamBrowser.Lockdown.Contracts;
using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
[Serializable]
internal abstract class ServiceConfiguration : FeatureConfiguration
{
private static readonly TimeSpan FIVE_SECONDS = TimeSpan.FromSeconds(5);
private const int MAX_ATTEMPTS = 5;
private IList<ServiceDataItem> originalItems;
protected abstract IEnumerable<ServiceConfigurationItem> Items { get; }
public ServiceConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
originalItems = new List<ServiceDataItem>();
}
public override bool DisableFeature()
{
var success = true;
foreach (var item in Items)
{
success &= TrySetWithRetry(new ServiceDataItem { Name = item.Name, Status = 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 &= TrySetWithRetry(new ServiceDataItem { Name = item.Name, Status = item.Enabled });
}
if (success)
{
logger.Info("Successfully enabled feature.");
}
else
{
logger.Warn("Failed to enable feature!");
}
return success;
}
public override FeatureConfigurationStatus GetStatus()
{
var status = FeatureConfigurationStatus.Undefined;
foreach (var item in Items)
{
var current = ReadService(item.Name);
if (current.Status == item.Disabled && status != FeatureConfigurationStatus.Enabled)
{
status = FeatureConfigurationStatus.Disabled;
}
else if (current.Status == item.Enabled && status != FeatureConfigurationStatus.Disabled)
{
status = FeatureConfigurationStatus.Enabled;
}
else
{
status = FeatureConfigurationStatus.Undefined;
break;
}
}
return status;
}
public override void Initialize()
{
foreach (var item in Items)
{
var original = ReadService(item.Name);
if (original.Status != ServiceStatus.NotAvailable)
{
originalItems.Add(original);
}
}
}
public override bool Restore()
{
foreach (var item in new List<ServiceDataItem>(originalItems))
{
if (TrySetWithRetry(item))
{
originalItems.Remove(item);
}
}
var success = !originalItems.Any();
if (success)
{
logger.Info("Successfully restored feature.");
}
else
{
logger.Warn("Failed to restore feature!");
}
return success;
}
private ServiceDataItem ReadService(string name)
{
var item = new ServiceDataItem { Name = name, Status = ServiceStatus.NotAvailable };
try
{
using (var service = new ServiceController(name))
{
item.Status = ToStatus(service.Status);
}
}
catch (Exception e)
{
logger.Error($"Failed to retrieve status of service '{name}'!", e);
}
return item;
}
private bool TrySetWithRetry(ServiceDataItem item)
{
var success = false;
if (IsAvailable(item))
{
for (var attempt = 1; attempt <= MAX_ATTEMPTS && !success; attempt++)
{
logger.Debug($"Attempt {attempt}/{MAX_ATTEMPTS} to set service {item}...");
success = TrySet(item);
}
if (!success)
{
logger.Error($"All attempts to set service {item} have failed!");
}
}
else
{
logger.Warn($"Cannot set service {item} as it does not exist!");
}
return success;
}
private bool TrySet(ServiceDataItem item)
{
var success = false;
try
{
using (var service = new ServiceController(item.Name))
{
if (item.Status == ServiceStatus.Running)
{
success = TryStart(service);
}
else if (item.Status == ServiceStatus.Stopped)
{
success = TryStop(service);
}
if (success)
{
logger.Debug($"Successfully set service {item}.");
}
else
{
logger.Warn($"Could not set service {item}! Current status: {service.Status}.");
}
}
}
catch (Exception e)
{
logger.Error($"Failed to set service {item}!", e);
}
return success;
}
private bool TryStart(ServiceController service)
{
var success = false;
if (service.Status == ServiceControllerStatus.PausePending)
{
service.WaitForStatus(ServiceControllerStatus.Paused, FIVE_SECONDS);
service.Refresh();
}
if (service.Status == ServiceControllerStatus.Paused)
{
service.Continue();
service.Refresh();
}
if (service.Status == ServiceControllerStatus.Stopped)
{
service.Start();
service.Refresh();
}
if (service.Status == ServiceControllerStatus.StartPending || service.Status == ServiceControllerStatus.ContinuePending)
{
service.WaitForStatus(ServiceControllerStatus.Running, FIVE_SECONDS);
service.Refresh();
}
if (service.Status == ServiceControllerStatus.Running)
{
success = true;
}
return success;
}
private bool TryStop(ServiceController service)
{
var success = false;
if (service.Status == ServiceControllerStatus.Paused)
{
service.Continue();
service.Refresh();
}
if (service.Status == ServiceControllerStatus.StartPending || service.Status == ServiceControllerStatus.ContinuePending)
{
service.WaitForStatus(ServiceControllerStatus.Running, FIVE_SECONDS);
service.Refresh();
}
if (service.Status == ServiceControllerStatus.Running)
{
service.Stop();
service.Refresh();
}
if (service.Status == ServiceControllerStatus.StopPending)
{
service.WaitForStatus(ServiceControllerStatus.Stopped, FIVE_SECONDS);
service.Refresh();
}
if (service.Status == ServiceControllerStatus.Stopped)
{
success = true;
}
return success;
}
private bool IsAvailable(ServiceDataItem item)
{
var available = false;
try
{
available = ServiceController.GetServices().Any(s => s.ServiceName == item.Name);
}
catch (Exception e)
{
logger.Error($"Failed to check whether service '{item.Name}' is available!", e);
}
return available;
}
private ServiceStatus ToStatus(ServiceControllerStatus status)
{
switch (status)
{
case ServiceControllerStatus.ContinuePending:
case ServiceControllerStatus.Running:
case ServiceControllerStatus.StartPending:
return ServiceStatus.Running;
default:
return ServiceStatus.Stopped;
}
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
internal class ServiceConfigurationItem
{
internal ServiceStatus Disabled { get; }
internal ServiceStatus Enabled { get; }
internal string Name { get; }
internal ServiceConfigurationItem(string name, ServiceStatus disabled, ServiceStatus enabled)
{
Disabled = disabled;
Enabled = enabled;
Name = name;
}
}
}

View File

@@ -0,0 +1,24 @@
/*
* 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;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
[Serializable]
internal class ServiceDataItem
{
internal string Name { get; set; }
internal ServiceStatus Status { get; set; }
public override string ToString()
{
return $@"'{Name}' => '{(Status == ServiceStatus.Running ? ServiceStatus.Running.ToString().ToLower() : ServiceStatus.Stopped.ToString().ToLower())}'";
}
}
}

View File

@@ -0,0 +1,17 @@
/*
* 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/.
*/
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
internal enum ServiceStatus
{
NotAvailable,
Running,
Stopped
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
[Serializable]
internal class WindowsUpdateConfiguration : ServiceConfiguration
{
protected override IEnumerable<ServiceConfigurationItem> Items => new []
{
new ServiceConfigurationItem("wuauserv", ServiceStatus.Stopped, ServiceStatus.Running)
};
public WindowsUpdateConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool Reset()
{
return EnableFeature();
}
}
}