SEBWIN-322: Implemented service configuration infrastructure and windows update service deactivation.

This commit is contained in:
dbuechel 2019-07-10 08:20:07 +02:00
parent 0d793dd326
commit 9dece7e4ae
9 changed files with 388 additions and 50 deletions

View file

@ -145,7 +145,7 @@ namespace SafeExamBrowser.Lockdown.UnitTests
Thread.Sleep(25); Thread.Sleep(25);
sut.Stop(); sut.Stop();
backup.Verify(b => b.GetAllConfigurations(), Times.Exactly(counter)); backup.Verify(b => b.GetAllConfigurations(), Times.Between(counter, counter + 1, Range.Inclusive));
} }
[TestMethod] [TestMethod]

View file

@ -9,9 +9,9 @@
using System; using System;
using SafeExamBrowser.Contracts.Lockdown; using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Lockdown.FeatureConfigurations;
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive; using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive;
using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive; using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.UserHive;
using SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations;
namespace SafeExamBrowser.Lockdown namespace SafeExamBrowser.Lockdown
{ {

View file

@ -0,0 +1,288 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations
{
[Serializable]
internal abstract class ServiceConfiguration : FeatureConfiguration
{
private static readonly TimeSpan FIVE_SECONDS = TimeSpan.FromSeconds(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 &= TrySet(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 &= TrySet(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 (TrySet(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 TrySet(ServiceDataItem item)
{
var success = false;
try
{
if (IsAvailable(item))
{
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}.");
}
}
}
else
{
logger.Warn($"Cannot set service {item} as it does not exist!");
}
}
catch (Exception e)
{
logger.Error($"Failed to set service {item}!", e);
}
return success;
}
private bool TryStart(ServiceController service)
{
var success = true;
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)
{
return ServiceController.GetServices().Any(s => s.ServiceName == item.Name);
}
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) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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,27 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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.Contracts.Logging;
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)
{
}
}
}

View file

@ -1,47 +0,0 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* 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 SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
[Serializable]
internal class WindowsUpdateConfiguration : FeatureConfiguration
{
public WindowsUpdateConfiguration(Guid groupId, ILogger logger) : base(groupId, logger)
{
}
public override bool DisableFeature()
{
return true;
}
public override bool EnableFeature()
{
return true;
}
public override FeatureConfigurationStatus GetStatus()
{
return FeatureConfigurationStatus.Undefined;
}
public override void Initialize()
{
}
public override bool Restore()
{
return true;
}
}
}

View file

@ -51,6 +51,7 @@
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
<Reference Include="System.ServiceProcess" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AutoRestoreMechanism.cs" /> <Compile Include="AutoRestoreMechanism.cs" />
@ -74,7 +75,11 @@
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\LockWorkstationConfiguration.cs" /> <Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\LockWorkstationConfiguration.cs" />
<Compile Include="FeatureConfigurations\RegistryConfigurations\MachineHive\SwitchUserConfiguration.cs" /> <Compile Include="FeatureConfigurations\RegistryConfigurations\MachineHive\SwitchUserConfiguration.cs" />
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\VmwareOverlayConfiguration.cs" /> <Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\VmwareOverlayConfiguration.cs" />
<Compile Include="FeatureConfigurations\WindowsUpdateConfiguration.cs" /> <Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceConfiguration.cs" />
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceConfigurationItem.cs" />
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceDataItem.cs" />
<Compile Include="FeatureConfigurations\ServiceConfigurations\ServiceStatus.cs" />
<Compile Include="FeatureConfigurations\ServiceConfigurations\WindowsUpdateConfiguration.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SystemConfigurationUpdate.cs" /> <Compile Include="SystemConfigurationUpdate.cs" />
</ItemGroup> </ItemGroup>