SEBWIN-313: Finished blacklist monitoring.
This commit is contained in:
parent
99aa595543
commit
de6cb5e75c
10 changed files with 151 additions and 75 deletions
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
|
@ -170,6 +171,7 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
actionCenter.QuitButtonClicked += Shell_QuitButtonClicked;
|
||||
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
|
||||
applicationMonitor.TerminationFailed += ApplicationMonitor_TerminationFailed;
|
||||
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
|
||||
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
|
||||
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
|
||||
|
@ -185,6 +187,7 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked;
|
||||
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
|
||||
applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed;
|
||||
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
|
||||
runtime.ConnectionLost -= Runtime_ConnectionLost;
|
||||
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
|
||||
|
@ -230,6 +233,13 @@ namespace SafeExamBrowser.Client
|
|||
logger.Info("Desktop successfully restored.");
|
||||
}
|
||||
|
||||
private void ApplicationMonitor_TerminationFailed(IEnumerable<RunningApplication> applications)
|
||||
{
|
||||
// foreach actionCenterActivator -> Pause
|
||||
// TODO: Show lock screen!
|
||||
// foreach actionCenterActivator -> Resume
|
||||
}
|
||||
|
||||
private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
|
||||
{
|
||||
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
internal class CompositionRoot
|
||||
{
|
||||
private const int TWO_SECONDS = 2000;
|
||||
private const int FIVE_SECONDS = 5000;
|
||||
|
||||
private Guid authenticationToken;
|
||||
|
@ -94,7 +95,7 @@ namespace SafeExamBrowser.Client
|
|||
taskbar = BuildTaskbar();
|
||||
terminationActivator = new TerminationActivator(ModuleLogger(nameof(TerminationActivator)));
|
||||
|
||||
var applicationMonitor = new ApplicationMonitor(FIVE_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
|
||||
var applicationMonitor = new ApplicationMonitor(TWO_SECONDS, ModuleLogger(nameof(ApplicationMonitor)), nativeMethods, new ProcessFactory(ModuleLogger(nameof(ProcessFactory))));
|
||||
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||
var hashAlgorithm = new HashAlgorithm();
|
||||
|
|
|
@ -14,6 +14,7 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
|||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Applications;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
|
@ -109,18 +110,18 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void StartMonitor()
|
||||
{
|
||||
//TODO: if (Context.Settings.KioskMode != KioskMode.None)
|
||||
//{
|
||||
if (Context.Settings.KioskMode != KioskMode.None)
|
||||
{
|
||||
applicationMonitor.Start();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private void StopMonitor()
|
||||
{
|
||||
//TODO: if (Context.Settings.KioskMode != KioskMode.None)
|
||||
//{
|
||||
if (Context.Settings.KioskMode != KioskMode.None)
|
||||
{
|
||||
applicationMonitor.Stop();
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult TryTerminate(IEnumerable<RunningApplication> runningApplications)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
namespace SafeExamBrowser.Monitoring.Contracts.Applications.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the Windows explorer process has started.
|
||||
/// Indicates that the Windows Explorer has been started.
|
||||
/// </summary>
|
||||
public delegate void ExplorerStartedEventHandler();
|
||||
}
|
||||
|
|
|
@ -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/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Contracts.Applications.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates that the given blacklisted applications could not be terminated.
|
||||
/// </summary>
|
||||
public delegate void TerminationFailedEventHandler(IEnumerable<RunningApplication> applications);
|
||||
}
|
|
@ -21,6 +21,11 @@ namespace SafeExamBrowser.Monitoring.Contracts.Applications
|
|||
/// </summary>
|
||||
event ExplorerStartedEventHandler ExplorerStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the automatic termination of a blacklisted application failed.
|
||||
/// </summary>
|
||||
event TerminationFailedEventHandler TerminationFailed;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the application monitor.
|
||||
/// </summary>
|
||||
|
|
|
@ -53,6 +53,7 @@
|
|||
<Reference Include="Microsoft.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Applications\Events\TerminationFailedEventHandler.cs" />
|
||||
<Compile Include="Applications\RunningApplication.cs" />
|
||||
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
|
||||
<Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
|
@ -32,6 +33,7 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
private IList<WhitelistApplication> whitelist;
|
||||
|
||||
public event ExplorerStartedEventHandler ExplorerStarted;
|
||||
public event TerminationFailedEventHandler TerminationFailed;
|
||||
|
||||
public ApplicationMonitor(int interval_ms, ILogger logger, INativeMethods nativeMethods, IProcessFactory processFactory)
|
||||
{
|
||||
|
@ -133,6 +135,74 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
return success;
|
||||
}
|
||||
|
||||
private void SystemEvent_WindowChanged(IntPtr window)
|
||||
{
|
||||
if (window != IntPtr.Zero && activeWindow != window)
|
||||
{
|
||||
logger.Debug($"Window has changed from {activeWindow} to {window}.");
|
||||
activeWindow = window;
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
if (!IsAllowed(window) && !TryHide(window))
|
||||
{
|
||||
Close(window);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var failed = new List<RunningApplication>();
|
||||
var running = processFactory.GetAllRunning();
|
||||
var started = running.Where(r => processes.All(p => p.Id != r.Id)).ToList();
|
||||
var terminated = processes.Where(p => running.All(r => r.Id != p.Id)).ToList();
|
||||
|
||||
foreach (var process in started)
|
||||
{
|
||||
logger.Debug($"Process {process} has been started.");
|
||||
processes.Add(process);
|
||||
|
||||
if (process.Name == "explorer")
|
||||
{
|
||||
HandleExplorerStart(process);
|
||||
}
|
||||
else if (!IsAllowed(process) && !TryTerminate(process))
|
||||
{
|
||||
AddFailed(process, failed);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var process in terminated)
|
||||
{
|
||||
logger.Debug($"Process {process} has been terminated.");
|
||||
processes.Remove(process);
|
||||
}
|
||||
|
||||
if (failed.Any())
|
||||
{
|
||||
logger.Warn($"Failed to terminate these blacklisted applications: {string.Join(", ", failed.Select(a => a.Name))}.");
|
||||
TerminationFailed?.Invoke(failed);
|
||||
}
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void AddFailed(IProcess process, List<RunningApplication> failed)
|
||||
{
|
||||
var name = blacklist.First(a => BelongsToApplication(process, a)).ExecutableName;
|
||||
var application = failed.FirstOrDefault(a => a.Name == name);
|
||||
|
||||
if (application == default(RunningApplication))
|
||||
{
|
||||
application = new RunningApplication(name);
|
||||
failed.Add(application);
|
||||
}
|
||||
|
||||
application.Processes.Add(process);
|
||||
}
|
||||
|
||||
private void AddFailed(string name, IProcess process, InitializationResult result)
|
||||
{
|
||||
var application = result.FailedAutoTerminations.FirstOrDefault(a => a.Name == name);
|
||||
|
@ -169,21 +239,6 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
return sameName || sameOriginalName;
|
||||
}
|
||||
|
||||
private void Check(IntPtr window)
|
||||
{
|
||||
var allowed = IsAllowed(window);
|
||||
|
||||
if (!allowed)
|
||||
{
|
||||
var success = TryHide(window);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Close(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Close(IntPtr window)
|
||||
{
|
||||
var title = nativeMethods.GetWindowTitle(window);
|
||||
|
@ -192,6 +247,23 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
logger.Info($"Sent close message to window '{title}' with handle = {window}.");
|
||||
}
|
||||
|
||||
private void HandleExplorerStart(IProcess process)
|
||||
{
|
||||
logger.Warn($"A new instance of Windows Explorer {process} has been started!");
|
||||
|
||||
if (!TryTerminate(process))
|
||||
{
|
||||
var application = new RunningApplication("Windows Explorer");
|
||||
|
||||
logger.Error("Failed to terminate new Windows Explorer instance!");
|
||||
application.Processes.Add(process);
|
||||
|
||||
Task.Run(() => TerminationFailed?.Invoke(new[] { application }));
|
||||
}
|
||||
|
||||
Task.Run(() => ExplorerStarted?.Invoke());
|
||||
}
|
||||
|
||||
private bool IsAllowed(IntPtr window)
|
||||
{
|
||||
var processId = nativeMethods.GetProcessIdFor(window);
|
||||
|
@ -213,6 +285,21 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
return true;
|
||||
}
|
||||
|
||||
private bool IsAllowed(IProcess process)
|
||||
{
|
||||
foreach (var application in blacklist)
|
||||
{
|
||||
if (BelongsToApplication(process, application))
|
||||
{
|
||||
logger.Warn($"Process {process} belongs to blacklisted application '{application.ExecutableName}'!");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryHide(IntPtr window)
|
||||
{
|
||||
var title = nativeMethods.GetWindowTitle(window);
|
||||
|
@ -265,51 +352,5 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
|
||||
return process.HasTerminated;
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var running = processFactory.GetAllRunning();
|
||||
var started = running.Where(r => processes.All(p => p.Id != r.Id)).ToList();
|
||||
var terminated = processes.Where(p => running.All(r => r.Id != p.Id)).ToList();
|
||||
|
||||
foreach (var process in started)
|
||||
{
|
||||
logger.Debug($"Process {process} has been started.");
|
||||
processes.Add(process);
|
||||
|
||||
foreach (var application in blacklist)
|
||||
{
|
||||
if (BelongsToApplication(process, application))
|
||||
{
|
||||
logger.Warn($"Process {process} belongs to blacklisted application '{application.ExecutableName}'! Attempting termination...");
|
||||
|
||||
var success = TryTerminate(process);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
// TODO: Invoke event -> Show lock screen!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var process in terminated)
|
||||
{
|
||||
logger.Debug($"Process {process} has been terminated.");
|
||||
processes.Remove(process);
|
||||
}
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void SystemEvent_WindowChanged(IntPtr window)
|
||||
{
|
||||
if (window != IntPtr.Zero && activeWindow != window)
|
||||
{
|
||||
logger.Debug($"Window has changed from {activeWindow} to {window}.");
|
||||
activeWindow = window;
|
||||
Check(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,7 +172,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
|
||||
proxy.Verify(p => p.InitiateShutdown(), Times.Once);
|
||||
proxy.Verify(p => p.Disconnect(), Times.Once);
|
||||
process.Verify(p => p.TryKill(default(int)), Times.Never);
|
||||
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Never);
|
||||
|
||||
Assert.IsNull(sessionContext.ClientProcess);
|
||||
Assert.IsNull(sessionContext.ClientProxy);
|
||||
|
@ -181,12 +181,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Revert_MustKillClientIfStoppingFailed()
|
||||
{
|
||||
process.Setup(p => p.TryKill(default(int))).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
|
||||
process.Setup(p => p.TryKill(It.IsAny<int>())).Callback(() => process.SetupGet(p => p.HasTerminated).Returns(true));
|
||||
|
||||
PerformNormally();
|
||||
sut.Revert();
|
||||
|
||||
process.Verify(p => p.TryKill(default(int)), Times.AtLeastOnce);
|
||||
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.AtLeastOnce);
|
||||
|
||||
Assert.IsNull(sessionContext.ClientProcess);
|
||||
Assert.IsNull(sessionContext.ClientProxy);
|
||||
|
@ -198,7 +198,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
PerformNormally();
|
||||
sut.Revert();
|
||||
|
||||
process.Verify(p => p.TryKill(default(int)), Times.Exactly(5));
|
||||
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Exactly(5));
|
||||
|
||||
Assert.IsNotNull(sessionContext.ClientProcess);
|
||||
Assert.IsNotNull(sessionContext.ClientProxy);
|
||||
|
@ -213,7 +213,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
|
||||
proxy.Verify(p => p.InitiateShutdown(), Times.Never);
|
||||
proxy.Verify(p => p.Disconnect(), Times.Never);
|
||||
process.Verify(p => p.TryKill(default(int)), Times.Never);
|
||||
process.Verify(p => p.TryKill(It.IsAny<int>()), Times.Never);
|
||||
|
||||
Assert.IsNull(sessionContext.ClientProcess);
|
||||
Assert.IsNull(sessionContext.ClientProxy);
|
||||
|
|
|
@ -248,7 +248,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
logger.Info($"Attempt {attempt}/{MAX_ATTEMPTS} to kill client process with ID = {ClientProcess.Id}.");
|
||||
|
||||
if (ClientProcess.TryKill())
|
||||
if (ClientProcess.TryKill(500))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue