From 1c42434b9a7602c5653eef41aec0d6e07b00e220 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Thu, 24 Nov 2022 14:50:25 +0100 Subject: [PATCH] SEBWIN-615: Implemented session integrity verification. --- .../ClientControllerTests.cs | 6 +- SafeExamBrowser.Client/ClientContext.cs | 6 + SafeExamBrowser.Client/ClientController.cs | 53 ++++++- SafeExamBrowser.Client/CompositionRoot.cs | 12 +- .../AppConfig.cs | 5 + .../Integrity/IIntegrityModule.cs | 15 ++ .../ConfigurationData/DataValues.cs | 4 +- .../Integrity/IntegrityModule.cs | 146 +++++++++++++++++- SafeExamBrowser.I18n.Contracts/TextKey.cs | 3 +- SafeExamBrowser.I18n/Data/de.xml | 7 +- SafeExamBrowser.I18n/Data/en.xml | 7 +- SafeExamBrowser.I18n/Data/fr.xml | 7 +- SafeExamBrowser.I18n/Data/it.xml | 7 +- SafeExamBrowser.I18n/Data/zh.xml | 13 +- SafeExamBrowser.Runtime/CompositionRoot.cs | 2 +- 15 files changed, 264 insertions(+), 29 deletions(-) diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index 6b7c9a24..f4c2be3b 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -76,6 +76,8 @@ namespace SafeExamBrowser.Client.UnitTests [TestInitialize] public void Initialize() { + var valid = true; + appConfig = new AppConfig(); actionCenter = new Mock(); applicationMonitor = new Mock(); @@ -101,6 +103,7 @@ namespace SafeExamBrowser.Client.UnitTests text = new Mock(); uiFactory = new Mock(); + integrityModule.Setup(m => m.TryVerifySessionIntegrity(It.IsAny(), It.IsAny(), out valid)).Returns(true); operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success); runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true)); uiFactory.Setup(u => u.CreateSplashScreen(It.IsAny())).Returns(new Mock().Object); @@ -113,7 +116,6 @@ namespace SafeExamBrowser.Client.UnitTests explorerShell.Object, fileSystemDialog.Object, hashAlgorithm.Object, - integrityModule.Object, logger.Object, messageBox.Object, operationSequence.Object, @@ -128,6 +130,7 @@ namespace SafeExamBrowser.Client.UnitTests context.AppConfig = appConfig; context.Browser = browser.Object; context.ClientHost = clientHost.Object; + context.IntegrityModule = integrityModule.Object; context.Server = server.Object; context.SessionId = sessionId; context.Settings = settings; @@ -1062,6 +1065,7 @@ namespace SafeExamBrowser.Client.UnitTests context.AppConfig = null; context.Browser = null; context.ClientHost = null; + context.IntegrityModule = null; context.Server = null; context.Settings = null; diff --git a/SafeExamBrowser.Client/ClientContext.cs b/SafeExamBrowser.Client/ClientContext.cs index 8b5a4cf7..1638afe3 100644 --- a/SafeExamBrowser.Client/ClientContext.cs +++ b/SafeExamBrowser.Client/ClientContext.cs @@ -12,6 +12,7 @@ using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Configuration.Contracts; +using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Server.Contracts; using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -48,6 +49,11 @@ namespace SafeExamBrowser.Client /// internal IClientHost ClientHost { get; set; } + /// + /// The integrity module. + /// + internal IIntegrityModule IntegrityModule { get; set; } + /// /// The server proxy to be used if the current session mode is . /// diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index 53c04883..d4128b72 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -12,6 +12,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading; +using System.Threading.Tasks; using SafeExamBrowser.Applications.Contracts; using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Browser.Contracts.Events; @@ -51,7 +52,6 @@ namespace SafeExamBrowser.Client private readonly IExplorerShell explorerShell; private readonly IFileSystemDialog fileSystemDialog; private readonly IHashAlgorithm hashAlgorithm; - private readonly IIntegrityModule integrityModule; private readonly ILogger logger; private readonly IMessageBox messageBox; private readonly IOperationSequence operations; @@ -65,6 +65,7 @@ namespace SafeExamBrowser.Client private IBrowserApplication Browser => context.Browser; private IClientHost ClientHost => context.ClientHost; + private IIntegrityModule IntegrityModule => context.IntegrityModule; private IServerProxy Server => context.Server; private AppSettings Settings => context.Settings; @@ -79,7 +80,6 @@ namespace SafeExamBrowser.Client IExplorerShell explorerShell, IFileSystemDialog fileSystemDialog, IHashAlgorithm hashAlgorithm, - IIntegrityModule integrityModule, ILogger logger, IMessageBox messageBox, IOperationSequence operations, @@ -98,7 +98,6 @@ namespace SafeExamBrowser.Client this.explorerShell = explorerShell; this.fileSystemDialog = fileSystemDialog; this.hashAlgorithm = hashAlgorithm; - this.integrityModule = integrityModule; this.logger = logger; this.messageBox = messageBox; this.operations = operations; @@ -137,6 +136,8 @@ namespace SafeExamBrowser.Client { logger.Info("Application successfully initialized."); logger.Log(string.Empty); + + VerifySessionIntegrity(); } else { @@ -165,6 +166,7 @@ namespace SafeExamBrowser.Client CloseShell(); DeregisterEvents(); + UpdateSessionIntegrity(); var success = operations.TryRevert() == OperationResult.Success; @@ -330,7 +332,7 @@ namespace SafeExamBrowser.Client { logger.Info($"Attempting to verify application integrity..."); - if (integrityModule.TryVerifyCodeSignature(out var isValid)) + if (IntegrityModule.TryVerifyCodeSignature(out var isValid)) { if (isValid) { @@ -339,7 +341,7 @@ namespace SafeExamBrowser.Client else { logger.Warn("Application integrity is compromised!"); - ShowLockScreen(text.Get(TextKey.LockScreen_IntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty()); + ShowLockScreen(text.Get(TextKey.LockScreen_ApplicationIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty()); } } else @@ -351,6 +353,47 @@ namespace SafeExamBrowser.Client timer.Start(); } + private void VerifySessionIntegrity() + { + var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash); + + if (hasQuitPassword) + { + logger.Info($"Attempting to verify session integrity..."); + + if (IntegrityModule.TryVerifySessionIntegrity(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl, out var isValid)) + { + if (isValid) + { + logger.Info("Session integrity successfully verified."); + IntegrityModule.CacheSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl); + } + else + { + logger.Warn("Session integrity is compromised!"); + Task.Delay(1000).ContinueWith(_ => + { + ShowLockScreen(text.Get(TextKey.LockScreen_SessionIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty()); + }); + } + } + else + { + logger.Warn("Failed to verify session integrity!"); + } + } + } + + private void UpdateSessionIntegrity() + { + var hasQuitPassword = !string.IsNullOrEmpty(Settings?.Security.QuitPasswordHash); + + if (hasQuitPassword) + { + IntegrityModule?.ClearSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl); + } + } + private void ApplicationMonitor_ExplorerStarted() { logger.Info("Trying to terminate Windows explorer..."); diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index d42391ad..944eb6bf 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -19,7 +19,6 @@ using SafeExamBrowser.Communication.Contracts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Communication.Hosts; using SafeExamBrowser.Communication.Proxies; -using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Configuration.Integrity; using SafeExamBrowser.Core.Contracts.OperationModel; @@ -71,7 +70,6 @@ namespace SafeExamBrowser.Client private UserInterfaceMode uiMode; private IActionCenter actionCenter; - private IIntegrityModule integrityModule; private ILogger logger; private IMessageBox messageBox; private INativeMethods nativeMethods; @@ -98,7 +96,6 @@ namespace SafeExamBrowser.Client actionCenter = uiFactory.CreateActionCenter(); context = new ClientContext(); - integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule))); messageBox = BuildMessageBox(); nativeMethods = new NativeMethods(); networkAdapter = new NetworkAdapter(ModuleLogger(nameof(NetworkAdapter)), nativeMethods); @@ -125,6 +122,7 @@ namespace SafeExamBrowser.Client operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken)); operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy)); operations.Enqueue(new DelegateOperation(UpdateAppConfig)); + operations.Enqueue(new DelegateOperation(BuildIntegrityModule)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation)); operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS)); operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation)); @@ -148,7 +146,6 @@ namespace SafeExamBrowser.Client explorerShell, fileSystemDialog, hashAlgorithm, - integrityModule, logger, messageBox, sequence, @@ -223,7 +220,7 @@ namespace SafeExamBrowser.Client private IOperation BuildBrowserOperation() { var fileSystemDialog = BuildFileSystemDialog(); - var keyGenerator = new KeyGenerator(context.AppConfig, integrityModule, ModuleLogger(nameof(KeyGenerator)), context.Settings); + var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)), context.Settings); var moduleLogger = ModuleLogger(nameof(BrowserApplication)); var browser = new BrowserApplication( context.AppConfig, @@ -256,6 +253,11 @@ namespace SafeExamBrowser.Client return operation; } + private void BuildIntegrityModule() + { + context.IntegrityModule = new IntegrityModule(context.AppConfig, ModuleLogger(nameof(IntegrityModule))); + } + private IOperation BuildKeyboardInterceptorOperation() { var keyboardInterceptor = new KeyboardInterceptor(ModuleLogger(nameof(KeyboardInterceptor)), nativeMethods, context.Settings.Keyboard); diff --git a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs index 44de3183..d21f0b37 100644 --- a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs +++ b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs @@ -186,6 +186,11 @@ namespace SafeExamBrowser.Configuration.Contracts /// public string ServiceLogFilePath { get; set; } + /// + /// The file path under which the session cache is to be stored. + /// + public string SessionCacheFilePath { get; set; } + /// /// The directory to be used for temporary application data. /// diff --git a/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs b/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs index 2c4b13e2..efdcd49d 100644 --- a/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs +++ b/SafeExamBrowser.Configuration.Contracts/Integrity/IIntegrityModule.cs @@ -13,6 +13,16 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity /// public interface IIntegrityModule { + /// + /// Caches the specified session for later integrity verification. + /// + void CacheSession(string configurationKey, string startUrl); + + /// + /// Removes the specified session from the integrity verification cache. + /// + void ClearSession(string configurationKey, string startUrl); + /// /// Attempts to calculate the browser exam key. /// @@ -22,5 +32,10 @@ namespace SafeExamBrowser.Configuration.Contracts.Integrity /// Attempts to verify the code signature. /// bool TryVerifyCodeSignature(out bool isValid); + + /// + /// Attempts to verify the integrity for the specified session. + /// + bool TryVerifySessionIntegrity(string configurationKey, string startUrl, out bool isValid); } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index 269d558d..09584488 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -45,6 +45,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData var appDataLocalFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser)); var appDataRoamingFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser)); var programDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser)); + var temporaryFolder = Path.Combine(appDataLocalFolder, "Temp"); var startTime = DateTime.Now; var logFolder = Path.Combine(appDataLocalFolder, "Logs"); var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); @@ -74,7 +75,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData appConfig.ServiceAddress = $"{AppConfig.BASE_ADDRESS}/service"; appConfig.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}"; appConfig.ServiceLogFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Service.log"); - appConfig.TemporaryDirectory = Path.Combine(appDataLocalFolder, "Temp"); + appConfig.SessionCacheFilePath = Path.Combine(temporaryFolder, "cache.bin"); + appConfig.TemporaryDirectory = temporaryFolder; return appConfig; } diff --git a/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs b/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs index 9cbcfb05..adccc846 100644 --- a/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs +++ b/SafeExamBrowser.Configuration/Integrity/IntegrityModule.cs @@ -7,7 +7,12 @@ */ using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; using System.Runtime.InteropServices; +using System.Security.Cryptography; +using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Integrity; using SafeExamBrowser.Logging.Contracts; @@ -15,20 +20,60 @@ namespace SafeExamBrowser.Configuration.Integrity { public class IntegrityModule : IIntegrityModule { - const string DLL_NAME = + private const string DLL_NAME = #if X86 "seb_x86.dll"; #else "seb_x64.dll"; #endif + private static readonly byte[] SESSION_DATA_IV = + { + 0x12, 0x07, 0x14, 0x02, 0x03, 0x10, 0x14, 0x18, + 0x11, 0x01, 0x04, 0x15, 0x06, 0x16, 0x05, 0x12 + }; + private static readonly byte[] SESSION_DATA_KEY = + { + 0x01, 0x04, 0x07, 0x08, 0x09, 0x10, 0x13, 0x06, + 0x11, 0x14, 0x15, 0x16, 0x05, 0x03, 0x13, 0x06, + 0x01, 0x04, 0x02, 0x03, 0x14, 0x15, 0x07, 0x08, + 0x11, 0x12, 0x16, 0x05, 0x09, 0x10, 0x12, 0x02 + }; + private static readonly string SESSION_DATA_SEPARATOR = "<@|--separator--|@>"; + + private readonly AppConfig appConfig; private readonly ILogger logger; - public IntegrityModule(ILogger logger) + public IntegrityModule(AppConfig appConfig, ILogger logger) { + this.appConfig = appConfig; this.logger = logger; } + public void CacheSession(string configurationKey, string startUrl) + { + if (TryReadSessionCache(out var sessions) && TryWriteSessionCache(sessions.Append((configurationKey, startUrl)))) + { + logger.Debug("Successfully cached session."); + } + else + { + logger.Error("Failed to cache session!"); + } + } + + public void ClearSession(string configurationKey, string startUrl) + { + if (TryReadSessionCache(out var sessions) && TryWriteSessionCache(sessions.Where(s => s.configurationKey != configurationKey && s.startUrl != startUrl))) + { + logger.Debug("Successfully cleared session."); + } + else + { + logger.Error("Failed to clear session!"); + } + } + public bool TryCalculateBrowserExamKey(string configurationKey, string salt, out string browserExamKey) { browserExamKey = default; @@ -72,6 +117,103 @@ namespace SafeExamBrowser.Configuration.Integrity return success; } + public bool TryVerifySessionIntegrity(string configurationKey, string startUrl, out bool isValid) + { + var success = false; + + isValid = false; + + if (TryReadSessionCache(out var sessions)) + { + isValid = sessions.All(s => s.configurationKey != configurationKey && s.startUrl != startUrl); + success = true; + logger.Debug($"Successfully verified session integrity, session is {(isValid ? "valid." : "compromised!")}"); + } + else + { + logger.Error("Failed to verify session integrity!"); + } + + return success; + } + + private bool TryReadSessionCache(out IList<(string configurationKey, string startUrl)> sessions) + { + var success = false; + + sessions = new List<(string configurationKey, string startUrl)>(); + + try + { + if (File.Exists(appConfig.SessionCacheFilePath)) + { + using (var file = new FileStream(appConfig.SessionCacheFilePath, FileMode.Open)) + using (var aes = Aes.Create()) + using (var stream = new CryptoStream(file, aes.CreateDecryptor(SESSION_DATA_KEY, SESSION_DATA_IV), CryptoStreamMode.Read)) + using (var reader = new StreamReader(stream)) + { + var line = reader.ReadLine(); + + if (line != default) + { + var session = line.Split(new string[] { SESSION_DATA_SEPARATOR }, StringSplitOptions.None); + var configurationKey = session[0]; + var startUrl = session[1]; + + sessions.Add((configurationKey, startUrl)); + } + } + } + + success = true; + } + catch (Exception e) + { + logger.Error("Failed to read session cache!", e); + } + + return success; + } + + private bool TryWriteSessionCache(IEnumerable<(string configurationKey, string startUrl)> sessions) + { + var success = false; + + try + { + if (sessions.Any()) + { + using (var file = new FileStream(appConfig.SessionCacheFilePath, FileMode.Create)) + using (var aes = Aes.Create()) + { + aes.Key = SESSION_DATA_KEY; + aes.IV = SESSION_DATA_IV; + + using (var stream = new CryptoStream(file, aes.CreateEncryptor(), CryptoStreamMode.Write)) + using (var writer = new StreamWriter(stream)) + { + foreach (var (configurationKey, startUrl) in sessions) + { + writer.WriteLine($"{configurationKey}{SESSION_DATA_SEPARATOR}{startUrl}"); + } + } + } + } + else + { + File.Delete(appConfig.SessionCacheFilePath); + } + + success = true; + } + catch (Exception e) + { + logger.Error("Failed to write session cache!", e); + } + + return success; + } + [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.BStr)] private static extern string CalculateBrowserExamKey(string configurationKey, string salt); diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 857bf079..82a2cc24 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -60,13 +60,14 @@ namespace SafeExamBrowser.I18n.Contracts FileSystemDialog_Select, FileSystemDialog_Title, FolderDialog_ApplicationLocation, + LockScreen_ApplicationIntegrityMessage, LockScreen_ApplicationsAllowOption, LockScreen_ApplicationsMessage, LockScreen_ApplicationsTerminateOption, LockScreen_DisplayConfigurationContinueOption, LockScreen_DisplayConfigurationTerminateOption, LockScreen_DisplayConfigurationMessage, - LockScreen_IntegrityMessage, + LockScreen_SessionIntegrityMessage, LockScreen_Title, LockScreen_UnlockButton, LockScreen_UserSessionContinueOption, diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml index fdb5346d..1f8457dd 100644 --- a/SafeExamBrowser.I18n/Data/de.xml +++ b/SafeExamBrowser.I18n/Data/de.xml @@ -138,6 +138,9 @@ Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt. + + Sie verwenden eine inoffizielle SEB-Version! Bitte stellen Sie sicher, dass Sie einen offiziellen Safe Exam Browser verwenden. Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren. + Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung! @@ -156,8 +159,8 @@ Eine verbotene Bildschirm-Konfiguration wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren. - - Sie verwenden eine inoffizielle SEB-Version! Bitte stellen Sie sicher, dass Sie einen offiziellen Safe Exam Browser verwenden. Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren. + + Die letzte Sitzung mit der momentan aktiven Konfiguration oder Start-URL wurde nicht ordnungsgemäss beendet! Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren. SEB GESPERRT diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml index 00bc9cc8..503c9c18 100644 --- a/SafeExamBrowser.I18n/Data/en.xml +++ b/SafeExamBrowser.I18n/Data/en.xml @@ -138,6 +138,9 @@ Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%". + + You are using an unofficial SEB version! Please make sure to use an official Safe Exam Browser. In order to unlock SEB, please enter the correct unlock password. + Temporarily allow the blacklisted applications. This applies only to the currently running instances and session! @@ -156,8 +159,8 @@ A prohibited display configuration has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password. - - You are using an unofficial SEB version! Please make sure to use an official Safe Exam Browser. In order to unlock SEB, please enter the correct unlock password. + + The last session with the currently active configuration or start URL was not terminated properly! Please enter the correct password to unlock SEB. SEB LOCKED diff --git a/SafeExamBrowser.I18n/Data/fr.xml b/SafeExamBrowser.I18n/Data/fr.xml index 64361edb..837e5016 100644 --- a/SafeExamBrowser.I18n/Data/fr.xml +++ b/SafeExamBrowser.I18n/Data/fr.xml @@ -138,6 +138,9 @@ L'application "%%NAME%%" n'a pas pu être trouvée sur le système! Veuillez localiser le dossier contenant l'exécutable principal "%%EXECUTABLE%%". + + Vous utilisez une version non officielle de SEB! Assurez-vous d'utiliser un Safe Exam Browser officiel. Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct. + Autoriser temporairement les applications figurant sur la liste noire. Cela ne s'applique qu'aux instances et à la session en cours! @@ -156,8 +159,8 @@ Une configuration d'affichage interdite a été détectée. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct. - - Vous utilisez une version non officielle de SEB! Assurez-vous d'utiliser un Safe Exam Browser officiel. Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct. + + La dernière session avec la configuration ou l'URL de démarrage actuellement active n'a pas été terminée correctement! Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct. SEB VEROUILLE diff --git a/SafeExamBrowser.I18n/Data/it.xml b/SafeExamBrowser.I18n/Data/it.xml index 40420605..af7ded6c 100644 --- a/SafeExamBrowser.I18n/Data/it.xml +++ b/SafeExamBrowser.I18n/Data/it.xml @@ -138,6 +138,9 @@ L'applicazione "%%NAME%%" non è stata trovata nel sistema! Individua la cartella contenente l'eseguibile principale "%%EXECUTABLE%%". + + Stai usando una versione SEB non ufficiale! Assicurati di utilizzare un Safe Exam Browser ufficiale. Per sbloccare SEB, inserisci la password di sblocco corretta. + Consenti temporaneamente le applicazioni nella lista nera. Ciò si applica solo alle istanze e alla sessione attualmente in esecuzione! @@ -156,8 +159,8 @@ È stata rilevata una configurazione di visualizzazione vietata. Per sbloccare SEB, seleziona una delle opzioni disponibili e inserisci la password di sblocco corretta. - - Stai usando una versione SEB non ufficiale! Assicurati di utilizzare un Safe Exam Browser ufficiale. Per sbloccare SEB, inserisci la password di sblocco corretta. + + L'ultima sessione con la configurazione o l'URL di avvio attualmente attiva non è stata terminata correttamente! Per sbloccare SEB, inserisci la password di sblocco corretta. SEB BLOCCATO diff --git a/SafeExamBrowser.I18n/Data/zh.xml b/SafeExamBrowser.I18n/Data/zh.xml index d3c262ef..364cc5c1 100644 --- a/SafeExamBrowser.I18n/Data/zh.xml +++ b/SafeExamBrowser.I18n/Data/zh.xml @@ -123,13 +123,16 @@ 无法在系统中找到应用程序 "%%NAME%%" 。请查找包含主可执行文件的文件夹"%%EXECUTABLE%%"。 - + + 您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB,请输入正确的解锁密码。 + + 暂时允许黑名单上的应用程序。这仅适用于当前正在运行的实例和会话。 - + 下面列出的黑名单应用程序已经启动,无法自动终止。请选择一个可用的选项并输入正确的密码来解锁防作弊考试专用浏览器。 - + 强制关闭防作弊考试专用浏览器。警告:将无法保存数据或执行任何进一步的操作,关闭操作将立即启动。 @@ -141,8 +144,8 @@ 检测到禁止的显示配置。 要解锁 SEB,请选择可用选项之一并输入正确的解锁密码。 - - 您使用的是非官方 SEB 版本! 请确保使用官方的安全考试浏览器。 要解锁 SEB,请输入正确的解锁密码。 + + 当前激活的配置或启动URL的最后一个会话没有被正确终止! 请输入正确的密码以解锁SEB。 防作弊考试专用浏览器已锁定 diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index b9701929..ffb272d3 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -64,7 +64,7 @@ namespace SafeExamBrowser.Runtime var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); var args = Environment.GetCommandLineArgs(); - var integrityModule = new IntegrityModule(ModuleLogger(nameof(IntegrityModule))); + var integrityModule = new IntegrityModule(appConfig, ModuleLogger(nameof(IntegrityModule))); var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory))); var desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor))); var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);