diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs
index 5e9dbdb1..30447485 100644
--- a/SafeExamBrowser.I18n.Contracts/TextKey.cs
+++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs
@@ -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,
diff --git a/SafeExamBrowser.I18n/Text.xml b/SafeExamBrowser.I18n/Text.xml
index 0bf19976..3524a0eb 100644
--- a/SafeExamBrowser.I18n/Text.xml
+++ b/SafeExamBrowser.I18n/Text.xml
@@ -222,6 +222,12 @@
Configuration Error
+
+ This computer appears to be a virtual machine. The currently active configuration does not allow SEB to be run in a virtual machine.
+
+
+ Virtual Machine Detected
+
Yes
@@ -309,6 +315,9 @@
Terminating user interface
+
+ Validating virtual machine policy
+
Waiting for Windows explorer to start up
diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/VirtualMachineOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/VirtualMachineOperationTests.cs
index 1a62c852..2afc2c0a 100644
--- a/SafeExamBrowser.Runtime.UnitTests/Operations/VirtualMachineOperationTests.cs
+++ b/SafeExamBrowser.Runtime.UnitTests/Operations/VirtualMachineOperationTests.cs
@@ -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 detector;
+ private Mock logger;
+ private SessionContext context;
+ private VirtualMachineOperation sut;
+
+ [TestInitialize]
+ public void Initialize()
{
- Assert.Fail();
+ detector = new Mock();
+ logger = new Mock();
+ 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);
}
}
}
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index ee375292..d18e0df4 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -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();
var sessionOperations = new Queue();
@@ -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));
diff --git a/SafeExamBrowser.Runtime/Operations/VirtualMachineOperation.cs b/SafeExamBrowser.Runtime/Operations/VirtualMachineOperation.cs
index 590d4823..ad66c5df 100644
--- a/SafeExamBrowser.Runtime/Operations/VirtualMachineOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/VirtualMachineOperation.cs
@@ -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;
+ }
}
}
diff --git a/SafeExamBrowser.SystemComponents.Contracts/IVirtualMachineDetector.cs b/SafeExamBrowser.SystemComponents.Contracts/IVirtualMachineDetector.cs
new file mode 100644
index 00000000..a291e651
--- /dev/null
+++ b/SafeExamBrowser.SystemComponents.Contracts/IVirtualMachineDetector.cs
@@ -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
+{
+ ///
+ /// Provides functionality related to virtual machine detection.
+ ///
+ public interface IVirtualMachineDetector
+ {
+ ///
+ /// Indicates whether the computer is in fact a virtual machine.
+ ///
+ bool IsVirtualMachine();
+ }
+}
diff --git a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj
index 876afa1a..d4a6a7ca 100644
--- a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj
+++ b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj
@@ -56,6 +56,7 @@
+
diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
index 9e7216b8..a4414c7f 100644
--- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
+++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj
@@ -71,6 +71,7 @@
+
diff --git a/SafeExamBrowser.SystemComponents/SystemInfo.cs b/SafeExamBrowser.SystemComponents/SystemInfo.cs
index f2bd3c16..77b83c58 100644
--- a/SafeExamBrowser.SystemComponents/SystemInfo.cs
+++ b/SafeExamBrowser.SystemComponents/SystemInfo.cs
@@ -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())
diff --git a/SafeExamBrowser.SystemComponents/VirtualMachineDetector.cs b/SafeExamBrowser.SystemComponents/VirtualMachineDetector.cs
new file mode 100644
index 00000000..9c19e020
--- /dev/null
+++ b/SafeExamBrowser.SystemComponents/VirtualMachineDetector.cs
@@ -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;
+ }
+ }
+}