SEBWIN-316: Implemented rudimentary VM detection.

This commit is contained in:
dbuechel 2020-01-06 15:11:57 +01:00
parent bc0170976c
commit 18fb059ddc
10 changed files with 240 additions and 13 deletions

View file

@ -88,6 +88,8 @@ namespace SafeExamBrowser.I18n.Contracts
MessageBox_StartupErrorTitle, MessageBox_StartupErrorTitle,
MessageBox_UnexpectedConfigurationError, MessageBox_UnexpectedConfigurationError,
MessageBox_UnexpectedConfigurationErrorTitle, MessageBox_UnexpectedConfigurationErrorTitle,
MessageBox_VirtualMachineNotAllowed,
MessageBox_VirtualMachineNotAllowedTitle,
MessageBox_YesButton, MessageBox_YesButton,
Notification_AboutTooltip, Notification_AboutTooltip,
Notification_LogTooltip, Notification_LogTooltip,
@ -117,6 +119,7 @@ namespace SafeExamBrowser.I18n.Contracts
OperationStatus_StopMouseInterception, OperationStatus_StopMouseInterception,
OperationStatus_TerminateBrowser, OperationStatus_TerminateBrowser,
OperationStatus_TerminateShell, OperationStatus_TerminateShell,
OperationStatus_ValidateVirtualMachinePolicy,
OperationStatus_WaitExplorerStartup, OperationStatus_WaitExplorerStartup,
OperationStatus_WaitExplorerTermination, OperationStatus_WaitExplorerTermination,
OperationStatus_WaitRuntimeDisconnection, OperationStatus_WaitRuntimeDisconnection,

View file

@ -222,6 +222,12 @@
<Entry key="MessageBox_UnexpectedConfigurationErrorTitle"> <Entry key="MessageBox_UnexpectedConfigurationErrorTitle">
Configuration Error Configuration Error
</Entry> </Entry>
<Entry key="MessageBox_VirtualMachineNotAllowed">
This computer appears to be a virtual machine. The currently active configuration does not allow SEB to be run in a virtual machine.
</Entry>
<Entry key="MessageBox_VirtualMachineNotAllowedTitle">
Virtual Machine Detected
</Entry>
<Entry key="MessageBox_YesButton"> <Entry key="MessageBox_YesButton">
Yes Yes
</Entry> </Entry>
@ -309,6 +315,9 @@
<Entry key="OperationStatus_TerminateShell"> <Entry key="OperationStatus_TerminateShell">
Terminating user interface Terminating user interface
</Entry> </Entry>
<Entry key="OperationStatus_ValidateVirtualMachinePolicy">
Validating virtual machine policy
</Entry>
<Entry key="OperationStatus_WaitExplorerStartup"> <Entry key="OperationStatus_WaitExplorerStartup">
Waiting for Windows explorer to start up Waiting for Windows explorer to start up
</Entry> </Entry>

View file

@ -7,16 +7,135 @@
*/ */
using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Security;
using SafeExamBrowser.SystemComponents.Contracts;
namespace SafeExamBrowser.Runtime.UnitTests.Operations namespace SafeExamBrowser.Runtime.UnitTests.Operations
{ {
[TestClass] [TestClass]
public class VirtualMachineOperationTests public class VirtualMachineOperationTests
{ {
[TestMethod] private Mock<IVirtualMachineDetector> detector;
public void TODO() private Mock<ILogger> logger;
private SessionContext context;
private VirtualMachineOperation sut;
[TestInitialize]
public void Initialize()
{ {
Assert.Fail(); detector = new Mock<IVirtualMachineDetector>();
logger = new Mock<ILogger>();
context = new SessionContext();
context.Next = new SessionConfiguration();
context.Next.Settings = new AppSettings();
sut = new VirtualMachineOperation(detector.Object, logger.Object, context);
}
[TestMethod]
public void Perform_MustAbortIfVirtualMachineNotAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Perform_MustSucceedIfVirtualMachineAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMostOnce);
Assert.AreEqual(OperationResult.Success, result);
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMost(2));
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Perform_MustSucceedIfNotAVirtualMachine()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
var result = sut.Perform();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustAbortIfVirtualMachineNotAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void Repeat_MustSucceedIfVirtualMachineAllowed()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(true);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMostOnce);
Assert.AreEqual(OperationResult.Success, result);
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Allow;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.AtMost(2));
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Repeat_MustSucceedIfNotAVirtualMachine()
{
context.Next.Settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
detector.Setup(d => d.IsVirtualMachine()).Returns(false);
var result = sut.Repeat();
detector.Verify(d => d.IsVirtualMachine(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
[TestMethod]
public void Revert_MustDoNothing()
{
var result = sut.Revert();
detector.VerifyNoOtherCalls();
logger.VerifyNoOtherCalls();
Assert.AreEqual(OperationResult.Success, result);
} }
} }
} }

View file

@ -74,6 +74,7 @@ namespace SafeExamBrowser.Runtime
var sessionContext = new SessionContext(); var sessionContext = new SessionContext();
var uiFactory = new UserInterfaceFactory(text); var uiFactory = new UserInterfaceFactory(text);
var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
var vmDetector = new VirtualMachineDetector(ModuleLogger(nameof(VirtualMachineDetector)), systemInfo);
var bootstrapOperations = new Queue<IOperation>(); var bootstrapOperations = new Queue<IOperation>();
var sessionOperations = new Queue<IRepeatableOperation>(); var sessionOperations = new Queue<IRepeatableOperation>();
@ -83,7 +84,7 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext)); sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext));
sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, new HashAlgorithm(), logger, sessionContext)); sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, new HashAlgorithm(), logger, sessionContext));
sessionOperations.Enqueue(new VirtualMachineOperation(sessionContext)); sessionOperations.Enqueue(new VirtualMachineOperation(vmDetector, logger, sessionContext));
sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo)); sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo));
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS)); sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext)); sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));

View file

@ -8,34 +8,65 @@
using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations.Events;
using SafeExamBrowser.Settings.Security;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
namespace SafeExamBrowser.Runtime.Operations namespace SafeExamBrowser.Runtime.Operations
{ {
internal class VirtualMachineOperation : SessionOperation internal class VirtualMachineOperation : SessionOperation
{ {
//private IVirtualMachineDetector detector; private IVirtualMachineDetector detector;
private ILogger logger;
public VirtualMachineOperation(/*IVirtualMachineDetector detector, */SessionContext context) : base(context) public VirtualMachineOperation(IVirtualMachineDetector detector, ILogger logger, SessionContext context) : base(context)
{ {
//this.detector = detector; this.detector = detector;
this.logger = logger;
} }
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event ActionRequiredEventHandler ActionRequired;
public override event StatusChangedEventHandler StatusChanged { add { } remove { } } public override event StatusChangedEventHandler StatusChanged;
public override OperationResult Perform() public override OperationResult Perform()
{ {
return OperationResult.Success; return ValidatePolicy();
} }
public override OperationResult Repeat() public override OperationResult Repeat()
{ {
return OperationResult.Success; return ValidatePolicy();
} }
public override OperationResult Revert() public override OperationResult Revert()
{ {
return OperationResult.Success; return OperationResult.Success;
} }
private OperationResult ValidatePolicy()
{
logger.Info($"Validating virtual machine policy...");
StatusChanged?.Invoke(TextKey.OperationStatus_ValidateVirtualMachinePolicy);
if (Context.Next.Settings.Security.VirtualMachinePolicy == VirtualMachinePolicy.Deny && detector.IsVirtualMachine())
{
var args = new MessageEventArgs
{
Icon = MessageBoxIcon.Error,
Message = TextKey.MessageBox_VirtualMachineNotAllowed,
Title = TextKey.MessageBox_VirtualMachineNotAllowedTitle
};
logger.Error("Detected virtual machine while SEB is not allowed to be run in a virtual machine! Aborting startup...");
ActionRequired?.Invoke(args);
return OperationResult.Aborted;
}
return OperationResult.Success;
}
} }
} }

View file

@ -0,0 +1,21 @@
/*
* 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.SystemComponents.Contracts
{
/// <summary>
/// Provides functionality related to virtual machine detection.
/// </summary>
public interface IVirtualMachineDetector
{
/// <summary>
/// Indicates whether the computer is in fact a virtual machine.
/// </summary>
bool IsVirtualMachine();
}
}

View file

@ -56,6 +56,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Audio\Events\VolumeChangedEventHandler.cs" /> <Compile Include="Audio\Events\VolumeChangedEventHandler.cs" />
<Compile Include="Audio\IAudio.cs" /> <Compile Include="Audio\IAudio.cs" />
<Compile Include="IVirtualMachineDetector.cs" />
<Compile Include="PowerSupply\Events\StatusChangedEventHandler.cs" /> <Compile Include="PowerSupply\Events\StatusChangedEventHandler.cs" />
<Compile Include="PowerSupply\IPowerSupply.cs" /> <Compile Include="PowerSupply\IPowerSupply.cs" />
<Compile Include="PowerSupply\BatteryChargeStatus.cs" /> <Compile Include="PowerSupply\BatteryChargeStatus.cs" />

View file

@ -71,6 +71,7 @@
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SystemInfo.cs" /> <Compile Include="SystemInfo.cs" />
<Compile Include="UserInfo.cs" /> <Compile Include="UserInfo.cs" />
<Compile Include="VirtualMachineDetector.cs" />
<Compile Include="WirelessNetwork\WirelessAdapter.cs" /> <Compile Include="WirelessNetwork\WirelessAdapter.cs" />
<Compile Include="WirelessNetwork\WirelessNetwork.cs" /> <Compile Include="WirelessNetwork\WirelessNetwork.cs" />
</ItemGroup> </ItemGroup>

View file

@ -45,8 +45,8 @@ namespace SafeExamBrowser.SystemComponents
private void InitializeMachineInfo() private void InitializeMachineInfo()
{ {
var model = string.Empty; var model = default(string);
var systemFamily = string.Empty; var systemFamily = default(string);
using (var searcher = new ManagementObjectSearcher("Select * from Win32_ComputerSystem")) using (var searcher = new ManagementObjectSearcher("Select * from Win32_ComputerSystem"))
using (var results = searcher.Get()) using (var results = searcher.Get())

View file

@ -0,0 +1,41 @@
/*
* 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 SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts;
namespace SafeExamBrowser.SystemComponents
{
public class VirtualMachineDetector : IVirtualMachineDetector
{
private ILogger logger;
private ISystemInfo systemInfo;
public VirtualMachineDetector(ILogger logger, ISystemInfo systemInfo)
{
this.logger = logger;
this.systemInfo = systemInfo;
}
public bool IsVirtualMachine()
{
var isVirtualMachine = false;
var manufacturer = systemInfo.Manufacturer.ToLower();
var model = systemInfo.Model.ToLower();
isVirtualMachine |= manufacturer.Contains("microsoft corporation") && !model.Contains("surface");
isVirtualMachine |= manufacturer.Contains("vmware");
isVirtualMachine |= manufacturer.Contains("parallels software");
isVirtualMachine |= model.Contains("virtualbox");
logger.Debug($"Computer '{systemInfo.Name}' appears to {(isVirtualMachine ? "" : "not ")}be a virtual machine.");
return isVirtualMachine;
}
}
}