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_UnexpectedConfigurationError,
MessageBox_UnexpectedConfigurationErrorTitle,
MessageBox_VirtualMachineNotAllowed,
MessageBox_VirtualMachineNotAllowedTitle,
MessageBox_YesButton,
Notification_AboutTooltip,
Notification_LogTooltip,
@ -117,6 +119,7 @@ namespace SafeExamBrowser.I18n.Contracts
OperationStatus_StopMouseInterception,
OperationStatus_TerminateBrowser,
OperationStatus_TerminateShell,
OperationStatus_ValidateVirtualMachinePolicy,
OperationStatus_WaitExplorerStartup,
OperationStatus_WaitExplorerTermination,
OperationStatus_WaitRuntimeDisconnection,

View file

@ -222,6 +222,12 @@
<Entry key="MessageBox_UnexpectedConfigurationErrorTitle">
Configuration Error
</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">
Yes
</Entry>
@ -309,6 +315,9 @@
<Entry key="OperationStatus_TerminateShell">
Terminating user interface
</Entry>
<Entry key="OperationStatus_ValidateVirtualMachinePolicy">
Validating virtual machine policy
</Entry>
<Entry key="OperationStatus_WaitExplorerStartup">
Waiting for Windows explorer to start up
</Entry>

View file

@ -7,16 +7,135 @@
*/
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
{
[TestClass]
public class VirtualMachineOperationTests
{
[TestMethod]
public void TODO()
private Mock<IVirtualMachineDetector> detector;
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 uiFactory = new UserInterfaceFactory(text);
var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo)));
var vmDetector = new VirtualMachineDetector(ModuleLogger(nameof(VirtualMachineDetector)), systemInfo);
var bootstrapOperations = new Queue<IOperation>();
var sessionOperations = new Queue<IRepeatableOperation>();
@ -83,7 +84,7 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, 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 ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
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.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
{
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 StatusChangedEventHandler StatusChanged { add { } remove { } }
public override event ActionRequiredEventHandler ActionRequired;
public override event StatusChangedEventHandler StatusChanged;
public override OperationResult Perform()
{
return OperationResult.Success;
return ValidatePolicy();
}
public override OperationResult Repeat()
{
return OperationResult.Success;
return ValidatePolicy();
}
public override OperationResult Revert()
{
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>
<Compile Include="Audio\Events\VolumeChangedEventHandler.cs" />
<Compile Include="Audio\IAudio.cs" />
<Compile Include="IVirtualMachineDetector.cs" />
<Compile Include="PowerSupply\Events\StatusChangedEventHandler.cs" />
<Compile Include="PowerSupply\IPowerSupply.cs" />
<Compile Include="PowerSupply\BatteryChargeStatus.cs" />

View file

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

View file

@ -45,8 +45,8 @@ namespace SafeExamBrowser.SystemComponents
private void InitializeMachineInfo()
{
var model = string.Empty;
var systemFamily = string.Empty;
var model = default(string);
var systemFamily = default(string);
using (var searcher = new ManagementObjectSearcher("Select * from Win32_ComputerSystem"))
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;
}
}
}