SEBWIN-309: Implemented scaffolding for Configuration Key.

This commit is contained in:
dbuechel 2020-01-10 08:54:10 +01:00
parent 45e1b001e3
commit 5e131289b0
8 changed files with 188 additions and 8 deletions

View file

@ -33,7 +33,7 @@ namespace SafeExamBrowser.Browser.Handlers
this.filter = filter;
this.logger = logger;
this.settings = settings;
this.resourceHandler = new ResourceHandler(appConfig, settings.Filter, filter, logger, text);
this.resourceHandler = new ResourceHandler(appConfig, settings, filter, logger, text);
}
protected override bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)

View file

@ -10,30 +10,34 @@ using System;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using CefSharp;
using SafeExamBrowser.Browser.Contracts.Filters;
using SafeExamBrowser.Browser.Filters;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Browser.Filter;
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
namespace SafeExamBrowser.Browser.Handlers
{
internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler
{
private AppConfig appConfig;
private FilterSettings settings;
private SHA256Managed algorithm;
private BrowserSettings settings;
private ILogger logger;
private IRequestFilter filter;
private IResourceHandler contentHandler;
private IResourceHandler pageHandler;
private IText text;
internal ResourceHandler(AppConfig appConfig, FilterSettings settings, IRequestFilter filter, ILogger logger, IText text)
internal ResourceHandler(AppConfig appConfig, BrowserSettings settings, IRequestFilter filter, ILogger logger, IText text)
{
this.appConfig = appConfig;
this.algorithm = new SHA256Managed();
this.filter = filter;
this.logger = logger;
this.settings = settings;
@ -52,14 +56,19 @@ namespace SafeExamBrowser.Browser.Handlers
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
{
if (IsMailtoUrl(request.Url))
{
return CefReturnValue.Cancel;
}
// TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser
// startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below
// reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development.
// AppendCustomUserAgent(request);
if (IsMailtoUrl(request.Url))
if (settings.SendCustomHeaders)
{
return CefReturnValue.Cancel;
AppendCustomHeaders(request);
}
ReplaceSebScheme(request);
@ -76,9 +85,22 @@ namespace SafeExamBrowser.Browser.Handlers
request.Headers = headers;
}
private void AppendCustomHeaders(IRequest request)
{
var headers = new NameValueCollection(request.Headers);
var urlWithoutFragment = request.Url.Split('#')[0];
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.HashValue));
var configurationKey = BitConverter.ToString(hash).Replace("-", string.Empty);
// TODO: Implement Browser Exam Key calculation.
// headers["X-SafeExamBrowser-RequestHash"] = ...;
headers["X-SafeExamBrowser-ConfigKeyHash"] = configurationKey;
request.Headers = headers;
}
private bool Block(IRequest request)
{
if (settings.ProcessContentRequests)
if (settings.Filter.ProcessContentRequests)
{
var result = filter.Process(new Request { Url = request.Url });
var block = result == FilterResult.Block;

View file

@ -96,6 +96,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
case Keys.Browser.ShowToolbar:
MapShowToolbar(settings, value);
break;
case Keys.Browser.SendCustomHeaders:
MapSendCustomHeaders(settings, value);
break;
case Keys.Browser.StartUrl:
MapStartUrl(settings, value);
break;
@ -275,6 +278,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
}
}
private void MapSendCustomHeaders(AppSettings settings, object value)
{
if (value is bool send)
{
settings.Browser.SendCustomHeaders = send;
}
}
private void MapStartUrl(AppSettings settings, object value)
{
if (value is string url)

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2020 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;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal class DataProcessor
{
internal void Process(IDictionary<string, object> rawData, AppSettings settings)
{
CalculateHashValue(rawData, settings);
}
private void CalculateHashValue(IDictionary<string, object> rawData, AppSettings settings)
{
using (var algorithm = new SHA256Managed())
using (var stream = new MemoryStream())
using (var writer = new StreamWriter(stream, Encoding.UTF8))
{
Serialize(rawData, writer);
writer.Flush();
stream.Seek(0, SeekOrigin.Begin);
var hash = algorithm.ComputeHash(stream);
var hashString = BitConverter.ToString(hash).Replace("-", string.Empty);
settings.Browser.HashValue = hashString;
}
}
private void Serialize(IDictionary<string, object> dictionary, StreamWriter stream)
{
var orderedByKey = dictionary.OrderBy(d => d.Key, StringComparer.OrdinalIgnoreCase).ToList();
stream.Write('{');
foreach (var kvp in orderedByKey)
{
var process = true;
process &= !kvp.Key.Equals(Keys.General.OriginatorVersion, StringComparison.OrdinalIgnoreCase);
process &= !(kvp.Value is IDictionary<string, object> d) || d.Any();
if (process)
{
stream.Write('"');
stream.Write(kvp.Key);
stream.Write('"');
stream.Write(':');
Serialize(kvp.Value, stream);
if (kvp.Key != orderedByKey.Last().Key)
{
stream.Write(',');
}
}
}
stream.Write('}');
}
private void Serialize(IList<object> list, StreamWriter stream)
{
stream.Write('[');
foreach (var item in list)
{
Serialize(item, stream);
if (item != list.Last())
{
stream.Write(',');
}
}
stream.Write(']');
}
private void Serialize(object value, StreamWriter stream)
{
switch (value)
{
case IDictionary<string, object> dictionary:
Serialize(dictionary, stream);
break;
case IList<object> list:
Serialize(list, stream);
break;
case byte[] data:
stream.Write('"');
stream.Write(Convert.ToBase64String(data));
stream.Write('"');
break;
case DateTime date:
stream.Write(date.ToString("o"));
break;
case bool boolean:
stream.Write(boolean.ToString().ToLower());
break;
case int integer:
stream.Write(integer.ToString(NumberFormatInfo.InvariantInfo));
break;
case double number:
stream.Write(number.ToString(NumberFormatInfo.InvariantInfo));
break;
case string text:
stream.Write('"');
stream.Write(text);
stream.Write('"');
break;
case null:
stream.Write('"');
stream.Write('"');
break;
}
}
}
}

View file

@ -52,6 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
internal const string QuitUrl = "quitURL";
internal const string QuitUrlConfirmation = "quitURLConfirm";
internal const string ShowToolbar = "enableBrowserWindowToolbar";
internal const string SendCustomHeaders = "sendBrowserExamKey";
internal const string StartUrl = "startURL";
internal const string UserAgentModeDesktop = "browserUserAgentWinDesktopMode";
internal const string UserAgentModeMobile = "browserUserAgentWinTouchMode";
@ -152,6 +153,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal const string AllowApplicationLog = "allowApplicationLog";
internal const string LogLevel = "logLevel";
internal const string OriginatorVersion = "originatorVersion";
}
internal static class Keyboard

View file

@ -15,8 +15,8 @@ using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Configuration.Contracts.DataResources;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration
{
@ -26,6 +26,7 @@ namespace SafeExamBrowser.Configuration
private IList<IDataParser> dataParsers;
private IList<IDataSerializer> dataSerializers;
private DataMapper dataMapper;
private DataProcessor dataProcessor;
private DataValues dataValues;
private IHashAlgorithm hashAlgorithm;
private ILogger logger;
@ -49,6 +50,7 @@ namespace SafeExamBrowser.Configuration
dataParsers = new List<IDataParser>();
dataSerializers = new List<IDataSerializer>();
dataMapper = new DataMapper();
dataProcessor = new DataProcessor();
dataValues = new DataValues(executablePath, programBuild, programCopyright, programTitle, programVersion);
resourceLoaders = new List<IResourceLoader>();
resourceSavers = new List<IResourceSaver>();
@ -144,6 +146,7 @@ namespace SafeExamBrowser.Configuration
if (status == LoadStatus.Success)
{
dataMapper.MapRawDataToSettings(data, settings);
dataProcessor.Process(data, settings);
}
}

View file

@ -65,6 +65,7 @@
<Compile Include="ConfigurationData\DataMapping\InputDataMapper.cs" />
<Compile Include="ConfigurationData\DataMapping\SecurityDataMapper.cs" />
<Compile Include="ConfigurationData\DataMapping\UserInterfaceDataMapper.cs" />
<Compile Include="ConfigurationData\DataProcessor.cs" />
<Compile Include="ConfigurationData\Keys.cs" />
<Compile Include="ConfigurationData\DataValues.cs" />
<Compile Include="Cryptography\CertificateStore.cs" />

View file

@ -51,6 +51,11 @@ namespace SafeExamBrowser.Settings.Browser
/// </summary>
public FilterSettings Filter { get; set; }
/// <summary>
/// The hash value of the raw settings data, used for integrity checks with server applications (see also <see cref="SendCustomHeaders"/>).
/// </summary>
public string HashValue { get; set; }
/// <summary>
/// The settings to be used for the main browser window.
/// </summary>
@ -71,6 +76,11 @@ namespace SafeExamBrowser.Settings.Browser
/// </summary>
public string QuitUrl { get; set; }
/// <summary>
/// Determines whether custom request headers (e.g. for integrity checks) are sent with every HTTP request.
/// </summary>
public bool SendCustomHeaders { get; set; }
/// <summary>
/// The URL with which the main browser window will be loaded.
/// </summary>