From b7c8f5f739d9a6988bfd1867805c51c464d81ea8 Mon Sep 17 00:00:00 2001 From: cedrozor Date: Fri, 30 Mar 2018 00:14:32 +0200 Subject: [PATCH] resynced FreeRDP with master repository (fixes the NLA issue introduced with Windows updates KB4088776, KB4088787, KB4088876, KB4088875) added support for MFA (one time password and one time host session url) (thanks Paul Oliver). Refer to documentation for activation added enterprise mode (AD pre-authentication and hosts list management) (thanks Paul Oliver). Refer to documentation for activation in enterprise mode, the security mode for the host connection is now configurable (NLA, TLS, RDP, etc.) most FreeRDP connection params are now configurable (myrtille\bin\Myrtille.Services.exe.config) the remote clipboard can now be disabled, for enhanced security (myrtille\Web.config) replaced client redirects by server redirects (cleaner and more friendly to proxies) added more connection details to myrtille logs --- CHANGELOG | 12 +- DISCLAIMERS.md | 2 +- DOCUMENTATION.md | 64 +- Myrtille.Common/Helpers/ClientIPHelper.cs | 154 +++ Myrtille.Common/Myrtille.Common.csproj | 1 + Myrtille.Common/Properties/AssemblyInfo.cs | 2 +- Myrtille.Enterprise/ActiveDirectory.cs | 579 +++++++++ Myrtille.Enterprise/Data/Host.cs | 19 + Myrtille.Enterprise/Data/HostAccessGroups.cs | 17 + .../Data/MigrationConfiguration.cs | 29 + .../Data/MyrtilleEnterpriseDBContext.cs | 18 + Myrtille.Enterprise/Data/Session.cs | 29 + Myrtille.Enterprise/Data/SessionGroup.cs | 18 + .../Migrations/Configuration.cs | 35 + .../Myrtille.Enterprise.csproj | 101 ++ .../Properties/AssemblyInfo.cs | 36 + Myrtille.Enterprise/packages.config | 6 + .../Myrtille.MFAProviders.csproj | 69 + Myrtille.MFAProviders/OASISOTPProvider.cs | 86 ++ .../Properties/AssemblyInfo.cs | 36 + Myrtille.MFAProviders/packages.config | 4 + .../Adapters/IEnterpriseAdapter.cs | 46 + .../IMultifactorAuthenticationAdapter.cs | 42 + .../IEnterpriseService.cs | 39 + .../IMFAAuthentication.cs | 20 + .../IRemoteSessionProcess.cs | 8 +- .../Models/EnterpriseConnectionDetails.cs | 8 + .../Models/EnterpriseHost.cs | 10 + .../Models/EnterpriseHostEdit.cs | 11 + .../Models/EnterpriseSession.cs | 9 + .../Models/SecurityProtocolEnum.cs | 11 + .../Myrtille.Services.Contracts.csproj | 9 + .../Properties/AssemblyInfo.cs | 2 +- Myrtille.Services/EnterpriseService.cs | 131 ++ Myrtille.Services/MFAAuthentication.cs | 27 + Myrtille.Services/Myrtille.Services.csproj | 35 + Myrtille.Services/Program.cs | 86 +- Myrtille.Services/Properties/AssemblyInfo.cs | 2 +- Myrtille.Services/RemoteSessionProcess.cs | 114 +- Myrtille.Services/app.config | 95 +- Myrtille.Services/packages.config | 3 + Myrtille.Setup/Myrtille.Setup.vdproj | 1112 +++++++++++++++-- Myrtille.Web/Default.aspx | 85 +- Myrtille.Web/Default.aspx.cs | 549 ++++++-- Myrtille.Web/Default.aspx.designer.cs | 92 +- Myrtille.Web/Global.asax.cs | 1 + Myrtille.Web/Myrtille.Web.csproj | 63 +- Myrtille.Web/Properties/AssemblyInfo.cs | 2 +- Myrtille.Web/PushUpdates.aspx.cs | 2 +- Myrtille.Web/Web.Base.config | 6 + Myrtille.Web/css/Default.css | 159 ++- Myrtille.Web/js/dialog.js | 63 +- Myrtille.Web/js/myrtille.js | 52 - Myrtille.Web/js/network/websocket.js | 2 +- Myrtille.Web/js/network/xmlhttp.js | 2 +- Myrtille.Web/popups/EditHost.aspx | 91 ++ Myrtille.Web/popups/EditHost.aspx.cs | 178 +++ Myrtille.Web/popups/EditHost.aspx.designer.cs | 78 ++ Myrtille.Web/popups/EditHostSession.aspx | 65 + Myrtille.Web/popups/EditHostSession.aspx.cs | 115 ++ .../popups/EditHostSession.aspx.designer.cs | 60 + Myrtille.Web/{ => popups}/FileStorage.aspx | 164 +-- Myrtille.Web/{ => popups}/FileStorage.aspx.cs | 352 +++--- .../{ => popups}/FileStorage.aspx.designer.cs | 102 +- Myrtille.Web/{ => popups}/ShowDialog.aspx | 138 +- Myrtille.Web/{ => popups}/ShowDialog.aspx.cs | 50 +- .../{ => popups}/ShowDialog.aspx.designer.cs | 30 +- .../{ => popups}/VirtualKeyboard.aspx | 128 +- .../{ => popups}/VirtualKeyboard.aspx.cs | 50 +- .../VirtualKeyboard.aspx.designer.cs | 30 +- Myrtille.Web/src/EnterpriseServiceClient.cs | 141 +++ Myrtille.Web/src/MFAAuthenticationClient.cs | 62 + Myrtille.Web/src/RemoteSession.cs | 6 +- Myrtille.Web/src/RemoteSessionPipes.cs | 2 +- .../src/RemoteSessionProcessClient.cs | 12 +- Myrtille.sln | 59 +- README.md | 5 + 77 files changed, 5217 insertions(+), 916 deletions(-) create mode 100644 Myrtille.Common/Helpers/ClientIPHelper.cs create mode 100644 Myrtille.Enterprise/ActiveDirectory.cs create mode 100644 Myrtille.Enterprise/Data/Host.cs create mode 100644 Myrtille.Enterprise/Data/HostAccessGroups.cs create mode 100644 Myrtille.Enterprise/Data/MigrationConfiguration.cs create mode 100644 Myrtille.Enterprise/Data/MyrtilleEnterpriseDBContext.cs create mode 100644 Myrtille.Enterprise/Data/Session.cs create mode 100644 Myrtille.Enterprise/Data/SessionGroup.cs create mode 100644 Myrtille.Enterprise/Migrations/Configuration.cs create mode 100644 Myrtille.Enterprise/Myrtille.Enterprise.csproj create mode 100644 Myrtille.Enterprise/Properties/AssemblyInfo.cs create mode 100644 Myrtille.Enterprise/packages.config create mode 100644 Myrtille.MFAProviders/Myrtille.MFAProviders.csproj create mode 100644 Myrtille.MFAProviders/OASISOTPProvider.cs create mode 100644 Myrtille.MFAProviders/Properties/AssemblyInfo.cs create mode 100644 Myrtille.MFAProviders/packages.config create mode 100644 Myrtille.Services.Contracts/Adapters/IEnterpriseAdapter.cs create mode 100644 Myrtille.Services.Contracts/Adapters/IMultifactorAuthenticationAdapter.cs create mode 100644 Myrtille.Services.Contracts/IEnterpriseService.cs create mode 100644 Myrtille.Services.Contracts/IMFAAuthentication.cs create mode 100644 Myrtille.Services.Contracts/Models/EnterpriseConnectionDetails.cs create mode 100644 Myrtille.Services.Contracts/Models/EnterpriseHost.cs create mode 100644 Myrtille.Services.Contracts/Models/EnterpriseHostEdit.cs create mode 100644 Myrtille.Services.Contracts/Models/EnterpriseSession.cs create mode 100644 Myrtille.Services.Contracts/Models/SecurityProtocolEnum.cs create mode 100644 Myrtille.Services/EnterpriseService.cs create mode 100644 Myrtille.Services/MFAAuthentication.cs create mode 100644 Myrtille.Web/popups/EditHost.aspx create mode 100644 Myrtille.Web/popups/EditHost.aspx.cs create mode 100644 Myrtille.Web/popups/EditHost.aspx.designer.cs create mode 100644 Myrtille.Web/popups/EditHostSession.aspx create mode 100644 Myrtille.Web/popups/EditHostSession.aspx.cs create mode 100644 Myrtille.Web/popups/EditHostSession.aspx.designer.cs rename Myrtille.Web/{ => popups}/FileStorage.aspx (95%) rename Myrtille.Web/{ => popups}/FileStorage.aspx.cs (95%) rename Myrtille.Web/{ => popups}/FileStorage.aspx.designer.cs (97%) rename Myrtille.Web/{ => popups}/ShowDialog.aspx (90%) rename Myrtille.Web/{ => popups}/ShowDialog.aspx.cs (96%) rename Myrtille.Web/{ => popups}/ShowDialog.aspx.designer.cs (96%) rename Myrtille.Web/{ => popups}/VirtualKeyboard.aspx (95%) rename Myrtille.Web/{ => popups}/VirtualKeyboard.aspx.cs (96%) rename Myrtille.Web/{ => popups}/VirtualKeyboard.aspx.designer.cs (96%) create mode 100644 Myrtille.Web/src/EnterpriseServiceClient.cs create mode 100644 Myrtille.Web/src/MFAAuthenticationClient.cs diff --git a/CHANGELOG b/CHANGELOG index 30e0a7f..74ebef4 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,14 @@ -2018-02-07 Version 1.7.0 (stable) +2018-03-29 Version 1.8.0 (beta) + resynced FreeRDP with master repository (fixes the NLA issue introduced with Windows updates KB4088776, KB4088787, KB4088876, KB4088875) + added support for MFA (one time password and one time host session url) (thanks Paul Oliver). Refer to documentation for activation + added enterprise mode (AD pre-authentication and hosts list management) (thanks Paul Oliver). Refer to documentation for activation + in enterprise mode, the security mode for the host connection is now configurable (NLA, TLS, RDP, etc.) + most FreeRDP connection params are now configurable (myrtille\bin\Myrtille.Services.exe.config) + the remote clipboard can now be disabled, for enhanced security (myrtille\Web.config) + replaced client redirects by server redirects (cleaner and more friendly to proxies) + added more connection details to myrtille logs + +2018-02-07 Version 1.7.0 (stable) resynced FreeRDP with master repository; FreeRDP 2.0-RC2 milestone: https://github.com/FreeRDP/FreeRDP/milestone/8 FreeRDP HOTFIX: connection using FQDN is broken (either direct connection or through a connection broker redirection), using target IP instead FreeRDP HOTFIX: fixed client IP resolution when using an ipv6 connection diff --git a/DISCLAIMERS.md b/DISCLAIMERS.md index 67b3cb3..2665c61 100644 --- a/DISCLAIMERS.md +++ b/DISCLAIMERS.md @@ -1,4 +1,4 @@ -## FreeRDP, Myrtille, Log4net +## FreeRDP, Myrtille, Log4net, OASIS Apache License Version 2.0, January 2004 diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 48f4884..8d954d6 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -1,4 +1,24 @@ -# Introduction +[Introduction](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#introduction) +[History](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#history) +[Installation](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#installation) +[Auto-connect / Start remote application from URL](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#auto-connect--start-remote-application-from-url) + [Syntax](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#syntax) + [Password Hash](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#password-hash) +[File transfer](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#file-transfer) +[Security](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#security) +[Configuration / Performance tweaks](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#configuration--performance-tweaks) +[Code organization](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#code-organization) +[Build](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#build) + [Startup projects](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#startup-projects) +[Communication](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#communication) + [Overview](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#overview) + [Protocols](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#protocols) +[Multifactor Authentication](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#multifactor-authentication) +[Enterprise Mode](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#enterprise-mode) +[Notes and limitations](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#notes-and-limitations) +[Troubleshoot](https://github.com/cedrozor/myrtille/blob/master/DOCUMENTATION.md#troubleshoot) + +# Introduction I worked hard to make Myrtille as straightforward as possible, with commented code, but some points may needs additional information. If you have any issue, question or suggestion that isn't addressed into this synthetic documentation, FAQ or Wiki, please don't hesitate to contact me (cedrozor@gmail.com) or ask the community (https://groups.google.com/forum/#!forum/myrtille_rdp). @@ -46,7 +66,6 @@ The pre version 1.5.0 syntax ("&password=*password*") is still supported, but it The parameters values **must be URL encoded**. You can use a tool like http://www.url-encode-decode.com/ (just copy & paste the encoded parameters into the URL). ### Password Hash - To generate a password hash, you can use the powershell script "password51.ps1" on the myrtille gateway (requires access to the machine). The script is located into the myrtille bin folder at runtime or into the "Myrtille.Services" project under Visual Studio. - Run the script (from its location folder): ". .\password51.ps1" (if needed, see powershell script execution policy: https://technet.microsoft.com/en-us/library/ee176961.aspx) - Call the encrypt function: "Encrypt-RDP-Password -Password *password*" @@ -145,6 +164,47 @@ However, I'm fully aware that it breaks the distributed architecture pattern. Th This is a thing to consider if you want to isolate the web gateway from your intranet (into a DMZ for instance) and still be able to connect a machine on it. +## Multifactor Authentication +Enabling this option allows you to require users to provide a one-time passcode which is validated against a cloud based 2fa authentication platform. This requires a mobile application which can be used to scan QR codes and generate the one-time passcodes; these applications are free from app stores and a popular choice is google authenticator. + +In addition to 2fa, an access control could be enforced to allow connections only from given areas (geo ip location) and time (not implemented for now). + +The adapter has been written to use a platform provided by Olive Innovations and named OASIS. It's free to use up to 10 users; to use this service follow these instructions: + + 1) Visit https://oasis.oliveinnovations.com and register for free + 2) Once logged in, create a User Group (into the menu select User Groups), then click New, input a Name (i.e. Myrtille) and save + 3) Create a user (choose Users from the menu), then click New, input user details and tick the box to send register by email (**IMPORTANT** the username must be the same as the username you will login with myrtille; when registering via email, the user will have a link to complete the registration) and save. Into the user details page, select the user group created in Step 2 + NOTE: If you have enabled Enterprise Mode and wish to sync your Active Directory with OASIS, visit http://www.oliveinnovations.com, go to download area and download the Gateway application; instructions for configuration can be found into the docs on the same website + 4) Create an application (choose Applications from the menu), then click New, enter a Name and save. You will be directed to the application details page, grant access to the user group created in Step 2 + 5) Within the application page, click the button Application Key, this will display the information to configure myrtille + +Once these steps are completed, edit the app.config file of Myrtille.Services and uncomment the following appSettings: + - `MFAAuthAdapter`, this is the OASIS MFA adapter + - `OASISApiKey`, this is the API Key found when you clicked Application Key in step 5 + - `OASISAppID`, this is the App ID found when you clicked Application Key in step 5 + - `OASISAppKey`, this is the App Key found when you clicked Application Key in step 5 + - Restart Myrtille.Services windows service to use the new settings + +The included MFA adapter is written by Olive Innovations Ltd for use with the OASIS platform. This adapter uses a nuget package, OASIS.Integration (https://www.nuget.org/packages/OASIS.Integration/) available open source (https://github.com/OliveInnovations/OASIS/). +If you wish to create your own MFA adapter, `Myrtille.Services.Contracts` contains the interfaces you need. + +## Enterprise Mode +When enabled, the enterprise mode authenticates users against a domain and allows administrators to create hosts connections which can be restricted to the security groups the authenticated users belongs to. + +The enterprise mode provides the following additional features: + - Authenticate users against a domain/active directory instead of a host they wish to connect to + - Allow administrators to define a list of hosts; these hosts are the only hosts the users can connect to + - Access to hosts can be restricted based on the groups the authenticated users belongs to + - Administrators can create a single use session url to a specific host (with specific login credentials) which can be shared with external (non domain) users and only be used once + +To enable enterprise mode, edit the app.config file of Myrtille.Services and uncomment the following appSettings: + - `EnterpriseAdapter`, this is the adapter to use for enterprise mode + - `EnterpriseAdminGroup`, this is the security group which will define a user as an administrator who can create, edit, delete hosts, define access to hosts and create single use sessions + - `EnterpriseDomain`, this is the NETBIOS name (i.e. MYDOMAIN) or FQDN (i.e. mydomain.local) of your domain + - Restart Myrtille.Services windows service to use the new settings + +If you wish to create your own enterprise adapter (with a different authentication, database or behavior), `Myrtille.Services.Contracts` contains the interfaces you need. + ## Notes and limitations - Starting from myrtille version 1.2.0, the packaged FreeRDP and OpenSSL binaries use a statically-linked runtime; that means there is no longer need for the Microsoft Visual C++ redistributables (x86). It's still a good idea to install them however as they will be required if the build options are changed. diff --git a/Myrtille.Common/Helpers/ClientIPHelper.cs b/Myrtille.Common/Helpers/ClientIPHelper.cs new file mode 100644 index 0000000..4894ea2 --- /dev/null +++ b/Myrtille.Common/Helpers/ClientIPHelper.cs @@ -0,0 +1,154 @@ +using System; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Web; + +namespace Myrtille.Helpers +{ + public static class ClientIPHelper + { + // based on http://www.grantburton.com/2008/11/30/fix-for-incorrect-ip-addresses-in-wordpress-comments/ + public static string ClientIPFromRequest(this HttpRequestBase request, bool skipPrivate, string[] ignoreAddresses) + { + foreach (var item in s_HeaderItems) + { + var ipString = (item.ServerVariable ? request.ServerVariables[item.Key] : request.Headers[item.Key]); + + if (String.IsNullOrEmpty(ipString)) + continue; + + if (item.Split) + { + foreach (var ip in ipString.Split(',')) + if (ValidIP(ip, skipPrivate) && !ignoreAddresses.Contains(ip)) + return ip; + } + else + { + if (ValidIP(ipString, skipPrivate) && !ignoreAddresses.Contains(ipString)) + return ipString; + } + } + + return request.UserHostAddress; + } + + private static bool ValidIP(string ip, bool skipPrivate) + { + IPAddress ipAddr; + + ip = ip == null ? String.Empty : ip.Trim(); + + if (0 == ip.Length + || false == IPAddress.TryParse(ip, out ipAddr) + || (ipAddr.AddressFamily != AddressFamily.InterNetwork + && ipAddr.AddressFamily != AddressFamily.InterNetworkV6)) + return false; + + if (skipPrivate && ipAddr.AddressFamily == AddressFamily.InterNetwork) + { + var addr = IpRange.AddrToUInt64(ipAddr); + foreach (var range in s_PrivateRanges) + { + if (range.Encompasses(addr)) + return false; + } + } + + return true; + } + + /// + /// Provides a simple class that understands how to parse and + /// compare IP addresses (IPV4) ranges. + /// + private sealed class IpRange + { + private readonly UInt64 _start; + private readonly UInt64 _end; + + public IpRange(string startStr, string endStr) + { + _start = ParseToUInt64(startStr); + _end = ParseToUInt64(endStr); + } + + public static UInt64 AddrToUInt64(IPAddress ip) + { + var ipBytes = ip.GetAddressBytes(); + UInt64 value = 0; + + foreach (var abyte in ipBytes) + { + value <<= 8; // shift + value += abyte; + } + + return value; + } + + public static UInt64 ParseToUInt64(string ipStr) + { + var ip = IPAddress.Parse(ipStr); + return AddrToUInt64(ip); + } + + public bool Encompasses(UInt64 addrValue) + { + return _start <= addrValue && addrValue <= _end; + } + + public bool Encompasses(IPAddress addr) + { + var value = AddrToUInt64(addr); + return Encompasses(value); + } + }; + + private static readonly IpRange[] s_PrivateRanges = + new IpRange[] { + new IpRange("0.0.0.0","2.255.255.255"), + new IpRange("10.0.0.0","10.255.255.255"), + new IpRange("127.0.0.0","127.255.255.255"), + new IpRange("169.254.0.0","169.254.255.255"), + new IpRange("172.16.0.0","172.31.255.255"), + new IpRange("192.0.2.0","192.0.2.255"), + new IpRange("192.168.0.0","192.168.255.255"), + new IpRange("255.255.255.0","255.255.255.255") + }; + + /// + /// Describes a header item (key) and if it is expected to be + /// a comma-delimited string + /// + private sealed class HeaderItem + { + public readonly string Key; + public readonly bool Split; + public readonly bool ServerVariable; + + public HeaderItem(string key, bool split, bool serverVariable) + { + Key = key; + Split = split; + ServerVariable = serverVariable; + } + } + + // order is in trust/use order top to bottom + private static readonly HeaderItem[] s_HeaderItems = + new HeaderItem[] { + new HeaderItem("X-Client-IP",false,true), + new HeaderItem("HTTP_CLIENT_IP",false,false), + new HeaderItem("X-Forwarded-For",true,true), + new HeaderItem("HTTP_X_FORWARDED_FOR",true,false), + new HeaderItem("HTTP_X_FORWARDED",false,false), + new HeaderItem("HTTP_X_CLUSTER_CLIENT_IP",false,false), + new HeaderItem("HTTP_FORWARDED_FOR",false,false), + new HeaderItem("HTTP_FORWARDED",false,false), + new HeaderItem("HTTP_VIA",false,false), + new HeaderItem("REMOTE_ADDR",false,false) + }; + } +} \ No newline at end of file diff --git a/Myrtille.Common/Myrtille.Common.csproj b/Myrtille.Common/Myrtille.Common.csproj index 6520cfd..0304b05 100644 --- a/Myrtille.Common/Myrtille.Common.csproj +++ b/Myrtille.Common/Myrtille.Common.csproj @@ -100,6 +100,7 @@ + diff --git a/Myrtille.Common/Properties/AssemblyInfo.cs b/Myrtille.Common/Properties/AssemblyInfo.cs index 685fba4..d0450e0 100644 --- a/Myrtille.Common/Properties/AssemblyInfo.cs +++ b/Myrtille.Common/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de révision et de build par défaut // en utilisant '*', comme indiqué ci-dessous : -[assembly: AssemblyVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.8.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Myrtille.Enterprise/ActiveDirectory.cs b/Myrtille.Enterprise/ActiveDirectory.cs new file mode 100644 index 0000000..4e848cf --- /dev/null +++ b/Myrtille.Enterprise/ActiveDirectory.cs @@ -0,0 +1,579 @@ +/* + Enterprise mode, mimic behaviour of RDP gateways + + Copyright(c) 2017-2018 Olive Innovations + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Collections.Generic; +using System.DirectoryServices; +using System.DirectoryServices.AccountManagement; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using Myrtille.Helpers; +using Myrtille.Services.Contracts; + +namespace Myrtille.Enterprise +{ + public class ActiveDirectory : IEnterpriseAdapter + { + public void Initialize() + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + db.Session.RemoveRange(db.Session); + db.SaveChanges(); + } + } + + /// + /// Authenticate user against an active directory + /// + /// + /// + /// + /// + /// + public EnterpriseSession Authenticate(string username, string password, string adminGroup, string domain) + { + try + { + using (var context = new PrincipalContext(ContextType.Domain, domain, username, password)) + { + UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username); + DirectoryEntry entry = (DirectoryEntry)user.GetUnderlyingObject(); + + var directoryGroups = new List(); + + try + { + directoryGroups.AddRange(user.GetGroups().Select(m => m.Name).ToList()); + } + catch (Exception e) + { + //There is an issue accessing user primary ad group remotely, + //Exception: Information about the domain could not be retrieved (1355). + //in that case use another method which will exclude the primary domain + // might need to find another way to do this! + directoryGroups.AddRange(GetDirectoryGroups(entry)); + } + + + bool isAdmin = directoryGroups.Any(m => m.Equals(adminGroup, StringComparison.InvariantCultureIgnoreCase)); + + string sessionID = Guid.NewGuid().ToString(); + string sessionKey = Guid.NewGuid().ToString("n"); + using (var db = new MyrtilleEnterpriseDBContext()) + { + var session = db.Session.FirstOrDefault(m => m.Username == username); + if (session != null) + { + db.Session.Remove(session); + db.SaveChanges(); + } + + session = new Session + { + Username = username, + Password = AES_Encrypt(RDPCryptoHelper.EncryptPassword(password), sessionKey), + SessionID = sessionID, + IsAdmin = isAdmin + }; + + db.Session.Add(session); + db.SaveChanges(); + + var groups = directoryGroups.Select(x => new SessionGroup + { + SessionID = session.ID, + DirectoryGroup = x + }); + + db.SessionGroup.AddRange(groups); + db.SaveChanges(); + return new EnterpriseSession + { + SessionID = sessionID, + SessionKey = sessionKey, + IsAdmin = isAdmin + + }; + } + } + } + catch (Exception e) + { + return null; + } + } + + /// + /// Delete user session + /// + /// + public void Logout(string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + var session = db.Session.FirstOrDefault(m => m.SessionID == sessionID); + + if (session != null) + { + db.Session.Remove(session); + db.SaveChanges(); + } + } + } + + /// + /// Add new host to the platform + /// + /// + /// + /// + public long? AddHost(EnterpriseHostEdit editHost, string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + if (!db.Session.Any(m => m.SessionID == sessionID && m.IsAdmin && m.Expire > DateTime.Now)) return null; + + if (db.Host.Any(m => m.HostName.Equals(editHost.HostName,StringComparison.InvariantCultureIgnoreCase))) return null; + + List groups = editHost.DirectoryGroups.Split(',').ToList(); + + var host = new Host + { + HostName = editHost.HostName, + HostAddress = editHost.HostAddress, + Protocol = editHost.Protocol + }; + + db.Host.Add(host); + + db.SaveChanges(); + + var hostAccess = groups.Select(x => new HostAccessGroups + { + HostID = host.ID, + AccessGroup = x.Trim() + }); + + db.HostAccessGroups.AddRange(hostAccess.Where(m => m.AccessGroup != "")); + db.SaveChanges(); + return host.ID; + } + } + + /// + /// Get host information from ID and sesssion ID + /// + /// + /// + /// Host information for connection or null if invalid hostid or sessionId specified + public EnterpriseHostEdit GetHost(long hostID, string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + if (!db.Session.Any(m => m.SessionID == sessionID && m.IsAdmin && m.Expire > DateTime.Now)) return null; + + var host = db.Host.FirstOrDefault(m => m.ID == hostID); + + if (host == null) return null; + + var directoryGroupList = db.HostAccessGroups + .Where(m => m.HostID == hostID) + .Select(m => m.AccessGroup) + .ToList(); + + StringBuilder directoryGroups = new StringBuilder(); + var isFirst = true; + foreach (string group in directoryGroupList) + { + if (!isFirst) directoryGroups.Append(", "); + isFirst = false; + directoryGroups.Append(group); + } + return new EnterpriseHostEdit + { + HostID = host.ID, + HostName = host.HostName, + HostAddress = host.HostAddress, + DirectoryGroups = directoryGroups.ToString() + , + Protocol = host.Protocol + }; + } + } + + /// + /// Update host information + /// + /// + /// + /// + public bool UpdateHost(EnterpriseHostEdit editHost, string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + if (!db.Session.Any(m => m.SessionID == sessionID && m.IsAdmin && m.Expire > DateTime.Now)) return false; + + var host = db.Host.FirstOrDefault(m => m.ID == editHost.HostID); + + host.HostName = editHost.HostName; + host.HostAddress = editHost.HostAddress; + host.Protocol = editHost.Protocol; + + var currentGroups = db.HostAccessGroups + .Where(m => m.HostID == editHost.HostID) + .ToList(); + + IEnumerable groups = editHost.DirectoryGroups.Split(',').ToList(); + + var hostsToDelete = currentGroups.Where(m => !groups.Any(p => p.Equals(m.AccessGroup, StringComparison.InvariantCultureIgnoreCase))); + + db.HostAccessGroups.RemoveRange(hostsToDelete); + + var hostAccess = groups + .Where(m => !currentGroups.Any(p => p.AccessGroup.Equals(m,StringComparison.InvariantCultureIgnoreCase))) + .Select(x => new HostAccessGroups + { + HostID = host.ID, + AccessGroup = x.Trim() + }); + + db.HostAccessGroups.AddRange(hostAccess.Where(m => m.AccessGroup != "")); + + db.SaveChanges(); + + return true; + } + } + + /// + /// Delete host + /// + /// + /// + /// + public bool DeleteHost(long hostID, string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + if (!db.Session.Any(m => m.SessionID == sessionID && m.IsAdmin && m.Expire > DateTime.Now)) return false; + + var host = db.Host.FirstOrDefault(m => m.ID == hostID); + + if (host == null) return false; + + db.Host.Remove(host); + db.SaveChanges(); + return true; + } + } + + /// + /// Get a list of active directory groups for a user + /// + /// + /// + private List GetDirectoryGroups(DirectoryEntry entry) + { + var directoryGroups = new List(); + + var groups = entry.Properties["memberOf"]; + + foreach (string group in groups) + { + var startIndex = group.IndexOf("CN="); + + if (startIndex < 0) + continue; + else + startIndex += 3; + + var endIndex = group.IndexOf("=", startIndex); + + if (endIndex < 0) + endIndex = group.Length - 3; + else + endIndex = endIndex - 6; + + var length = endIndex - startIndex; + + directoryGroups.Add(group.Substring(startIndex, endIndex)); + } + + return directoryGroups; + } + + /// + /// Retrieve a list of hosts the user session is allowed to access + /// + /// + /// + public List SessionHosts(string sessionID) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + var sessionInfo = db.Session + .Where(m => m.SessionID == sessionID && m.Expire > DateTime.Now) + .Select(m => new + { + SessionID = m.SessionID, + IsAdmin = m.IsAdmin + }) + .FirstOrDefault(); + + if (sessionInfo == null) return new List(); + + if (sessionInfo.IsAdmin) + { + return (from s in db.Session + from h in db.Host + where s.SessionID == sessionID + && s.Expire > DateTime.Now + select new EnterpriseHost + { + HostID = h.ID, + HostName = h.HostName, + HostAddress = h.HostAddress + }) + .Distinct() + .OrderBy(m => m.HostName) + .ToList(); + } + else + { + return (from s in db.Session + join sg in db.SessionGroup on s.ID equals sg.SessionID + join hag in db.HostAccessGroups on sg.DirectoryGroup equals hag.AccessGroup + join h in db.Host on hag.HostID equals h.ID + where s.SessionID == sessionID + && s.Expire > DateTime.Now + select new EnterpriseHost + { + HostID = h.ID, + HostName = h.HostName, + HostAddress = h.HostAddress + }) + .Distinct() + .OrderBy(m => m.HostName) + .ToList(); + } + } + } + + /// + /// Get the connection details for the session and host + /// + /// + /// + /// + /// + public EnterpriseConnectionDetails GetSessionConnectionDetails(string sessionID, long hostID, string sessionKey) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + var session = db.Session + .Where(m => m.SessionID == sessionID && m.Expire > DateTime.Now) + .Select(m => new + { + SessionID = m.SessionID + , + OneTime = m.OneTime + }) + .FirstOrDefault(); + + EnterpriseConnectionDetails result = null; + if (session != null) + { + if (session.OneTime) + { + result = (from s in db.Session + from h in db.Host + where s.SessionID == sessionID + && h.ID == hostID + && s.Expire > DateTime.Now + select new EnterpriseConnectionDetails + { + HostID = h.ID + , + HostName = h.HostName + , + HostAddress = h.HostAddress + , + Username = s.Username + , + Password = s.Password + , + Protocol = h.Protocol + }) + .FirstOrDefault(); + } + else + { + result = (from s in db.Session + join sg in db.SessionGroup on s.ID equals sg.SessionID + join hag in db.HostAccessGroups on sg.DirectoryGroup equals hag.AccessGroup + join h in db.Host on hag.HostID equals h.ID + where s.SessionID == sessionID + && h.ID == hostID + && s.Expire > DateTime.Now + select new EnterpriseConnectionDetails + { + HostID = h.ID + , + HostName = h.HostName + , + HostAddress = h.HostAddress + , + Username = s.Username + , + Password = s.Password + , + Protocol = h.Protocol + }) + .FirstOrDefault(); + } + + if (result != null) + { + result.Password = AES_Decrypt(result.Password, sessionKey); + } + + // when connected from the login page, the session logout is based on expiration or user action + // when connected from a one time url, the logout is done immediately + if (session.OneTime) + { + Logout(sessionID); + } + } + + return result; + } + } + + /// + /// Create a session, the session URL returned can be given to external users to connect to a specific host using a URL + /// + /// + /// + /// + /// + /// + public string CreateUserSession(string sessionID, long hostID, string username, string password) + { + using (var db = new MyrtilleEnterpriseDBContext()) + { + if (!db.Session.Any(m => m.SessionID == sessionID && m.IsAdmin && m.Expire > DateTime.Now)) return null; + + if (!db.Host.Any(m => m.ID == hostID)) return null; + + string newSessionID = Guid.NewGuid().ToString(); + string sessionKey = Guid.NewGuid().ToString("n"); + + var session = new Session + { + Username = username, + Password = AES_Encrypt(RDPCryptoHelper.EncryptPassword(password), sessionKey), + SessionID = newSessionID, + IsAdmin = false, + Expire = DateTime.Now.AddHours(1), + OneTime = true + }; + + db.Session.Add(session); + db.SaveChanges(); + + return string.Format("?SI={0}&SD={1}&SK={2}",newSessionID,hostID,sessionKey); + } + } + + #region aes encryption + + private static string AES_Encrypt(string stringToBeEncrypted, string passwordString) + { + string encrypted; + byte[] passwordBytes = Encoding.UTF8.GetBytes(passwordString); + byte[] bytesToBeEncrypted = Encoding.UTF8.GetBytes(stringToBeEncrypted); + + byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + + using (MemoryStream ms = new MemoryStream()) + { + using (RijndaelManaged AES = new RijndaelManaged()) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write)) + { + cs.Write(bytesToBeEncrypted, 0, bytesToBeEncrypted.Length); + cs.Close(); + } + encrypted = Convert.ToBase64String(ms.ToArray()); + + } + } + + return encrypted; + } + + public string AES_Decrypt(string stringToBeDecrypted, string passwordString) + { + string decryptedString; + byte[] bytesToBeDecrypted = Convert.FromBase64String(stringToBeDecrypted); + byte[] passwordBytes = Encoding.UTF8.GetBytes(passwordString); + + // Set your salt here, change it to meet your flavor: + // The salt bytes must be at least 8 bytes. + byte[] saltBytes = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }; + + using (MemoryStream ms = new MemoryStream()) + { + using (RijndaelManaged AES = new RijndaelManaged()) + { + AES.KeySize = 256; + AES.BlockSize = 128; + + var key = new Rfc2898DeriveBytes(passwordBytes, saltBytes, 1000); + AES.Key = key.GetBytes(AES.KeySize / 8); + AES.IV = key.GetBytes(AES.BlockSize / 8); + + AES.Mode = CipherMode.CBC; + + using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write)) + { + cs.Write(bytesToBeDecrypted, 0, bytesToBeDecrypted.Length); + cs.Close(); + } + decryptedString = Encoding.UTF8.GetString(ms.ToArray()); + } + } + + return decryptedString; + } + + #endregion + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/Host.cs b/Myrtille.Enterprise/Data/Host.cs new file mode 100644 index 0000000..498d64a --- /dev/null +++ b/Myrtille.Enterprise/Data/Host.cs @@ -0,0 +1,19 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Myrtille.Services.Contracts; + +namespace Myrtille.Enterprise +{ + public class Host + { + [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long ID { get; set; } + + [StringLength(250),DataType("varchar")] + public string HostName { get; set; } + + public string HostAddress { get; set; } + + public SecurityProtocolEnum Protocol { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/HostAccessGroups.cs b/Myrtille.Enterprise/Data/HostAccessGroups.cs new file mode 100644 index 0000000..e5ac73e --- /dev/null +++ b/Myrtille.Enterprise/Data/HostAccessGroups.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Myrtille.Enterprise +{ + public class HostAccessGroups + { + [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long ID { get; set; } + + public long HostID { get; set; } + [ForeignKey("HostID")] + public virtual Host Host { get; set; } + + public string AccessGroup { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/MigrationConfiguration.cs b/Myrtille.Enterprise/Data/MigrationConfiguration.cs new file mode 100644 index 0000000..197cac2 --- /dev/null +++ b/Myrtille.Enterprise/Data/MigrationConfiguration.cs @@ -0,0 +1,29 @@ +using System.Data.Entity.Migrations; + +namespace Myrtille.Enterprise +{ + internal sealed class MigrationConfiguration : DbMigrationsConfiguration + { + public MigrationConfiguration() + { + AutomaticMigrationsEnabled = true; + AutomaticMigrationDataLossAllowed = true; + } + + protected override void Seed(Myrtille.Enterprise.MyrtilleEnterpriseDBContext context) + { + // This method will be called after migrating to the latest version. + + // You can use the DbSet.AddOrUpdate() helper extension method + // to avoid creating duplicate seed data. E.g. + // + // context.People.AddOrUpdate( + // p => p.FullName, + // new Person { FullName = "Andrew Peters" }, + // new Person { FullName = "Brice Lambson" }, + // new Person { FullName = "Rowan Miller" } + // ); + // + } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/MyrtilleEnterpriseDBContext.cs b/Myrtille.Enterprise/Data/MyrtilleEnterpriseDBContext.cs new file mode 100644 index 0000000..2bbcdf2 --- /dev/null +++ b/Myrtille.Enterprise/Data/MyrtilleEnterpriseDBContext.cs @@ -0,0 +1,18 @@ +using System.Data.Entity; +using System.Data.SqlServerCe; + +namespace Myrtille.Enterprise +{ + public class MyrtilleEnterpriseDBContext : DbContext + { + public MyrtilleEnterpriseDBContext() : base(new SqlCeConnection(@"Data Source=|DataDirectory|MyrtilleEnterprise.sdf;Persist Security Info=False;"), contextOwnsConnection: true) + { + Database.SetInitializer(new MigrateDatabaseToLatestVersion()); + } + + public virtual DbSet Session { get; set; } + public virtual DbSet SessionGroup { get; set; } + public virtual DbSet Host { get; set; } + public virtual DbSet HostAccessGroups { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/Session.cs b/Myrtille.Enterprise/Data/Session.cs new file mode 100644 index 0000000..e134788 --- /dev/null +++ b/Myrtille.Enterprise/Data/Session.cs @@ -0,0 +1,29 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Myrtille.Enterprise +{ + public class Session + { + public Session() { Expire = DateTime.Now.AddHours(12); OneTime = false; } + + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long ID { get; set; } + + [Index(IsClustered = true), StringLength(100)] + public string SessionID { get; set; } + + [Index, StringLength(250)] + public string Username { get; set; } + + [StringLength(2000)] + public string Password { get; set; } + + public bool IsAdmin { get; set; } + + public DateTime Expire { get; set; } + + public bool OneTime { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Data/SessionGroup.cs b/Myrtille.Enterprise/Data/SessionGroup.cs new file mode 100644 index 0000000..ee4f442 --- /dev/null +++ b/Myrtille.Enterprise/Data/SessionGroup.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace Myrtille.Enterprise +{ + public class SessionGroup + { + [Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public long ID { get; set; } + + public long SessionID { get; set; } + [ForeignKey("SessionID")] + public virtual Session Session { get; set; } + + [StringLength(250)] + public string DirectoryGroup { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Migrations/Configuration.cs b/Myrtille.Enterprise/Migrations/Configuration.cs new file mode 100644 index 0000000..8ec6671 --- /dev/null +++ b/Myrtille.Enterprise/Migrations/Configuration.cs @@ -0,0 +1,35 @@ +using System.Data.Entity.Migrations; + +namespace Myrtille.Enterprise.Migrations +{ + internal sealed class Configuration : DbMigrationsConfiguration + { + public Configuration() + { + AutomaticMigrationsEnabled = true; + AutomaticMigrationDataLossAllowed = true; + + //var migrator = new DbMigrator(this); + //if (migrator.GetPendingMigrations().Any()) + //{ + // migrator.Update(); + //} + } + + protected override void Seed(MyrtilleEnterpriseDBContext context) + { + // This method will be called after migrating to the latest version. + + // You can use the DbSet.AddOrUpdate() helper extension method + // to avoid creating duplicate seed data. E.g. + // + // context.People.AddOrUpdate( + // p => p.FullName, + // new Person { FullName = "Andrew Peters" }, + // new Person { FullName = "Brice Lambson" }, + // new Person { FullName = "Rowan Miller" } + // ); + // + } + } +} \ No newline at end of file diff --git a/Myrtille.Enterprise/Myrtille.Enterprise.csproj b/Myrtille.Enterprise/Myrtille.Enterprise.csproj new file mode 100644 index 0000000..fa7dd52 --- /dev/null +++ b/Myrtille.Enterprise/Myrtille.Enterprise.csproj @@ -0,0 +1,101 @@ + + + + + Debug + AnyCPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4} + Library + Properties + Myrtille.Enterprise + Myrtille.Enterprise + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + + + ..\packages\EntityFramework.SqlServerCompact.6.2.0\lib\net45\EntityFramework.SqlServerCompact.dll + True + + + + + + ..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll + True + + + + + + + + + + + + + + + + + + + + + + + + + + + {37630774-1321-4e6a-8661-4430a8946e9e} + Myrtille.Common + + + {010e1702-3045-4b13-bfb6-06ffc60b5cbb} + Myrtille.Services.Contracts + + + + + + + if not exist "$(TargetDir)x86" md "$(TargetDir)x86" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" + if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" + + + \ No newline at end of file diff --git a/Myrtille.Enterprise/Properties/AssemblyInfo.cs b/Myrtille.Enterprise/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d60c3f7 --- /dev/null +++ b/Myrtille.Enterprise/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Myrtille.Enterprise")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Myrtille.Enterprise")] +[assembly: AssemblyCopyright("Copyright © Olive Innovations Ltd 2017-2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9e4bf7c3-8ea3-428c-bea3-06cc3afcaeb4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.8.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Myrtille.Enterprise/packages.config b/Myrtille.Enterprise/packages.config new file mode 100644 index 0000000..82f88f3 --- /dev/null +++ b/Myrtille.Enterprise/packages.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Myrtille.MFAProviders/Myrtille.MFAProviders.csproj b/Myrtille.MFAProviders/Myrtille.MFAProviders.csproj new file mode 100644 index 0000000..9eb1c15 --- /dev/null +++ b/Myrtille.MFAProviders/Myrtille.MFAProviders.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF} + Library + Properties + Myrtille.MFAProviders + Myrtille.MFAProviders + v4.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\OASIS.Integration.1.6.1\lib\OASIS.Integration.dll + True + + + + + + + + + + + + + + + + + + + + + {010e1702-3045-4b13-bfb6-06ffc60b5cbb} + Myrtille.Services.Contracts + + + + + \ No newline at end of file diff --git a/Myrtille.MFAProviders/OASISOTPProvider.cs b/Myrtille.MFAProviders/OASISOTPProvider.cs new file mode 100644 index 0000000..5d48abc --- /dev/null +++ b/Myrtille.MFAProviders/OASISOTPProvider.cs @@ -0,0 +1,86 @@ +/* + OASIS One time passcode service integration + + Copyright(c) 2017-2018 Olive Innovations + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Configuration; +using OASIS.Integration; +using OASIS.Integration.Models; +using Myrtille.Services.Contracts; + +namespace Myrtille.MFAProviders +{ + // OASIS OTP Provider is released under Apache 2.0 project. + // Source available at https://github.com/oliveinnovations/oasis + + public class OASISOTPProvider : IMultifactorAuthenticationAdapter + { + /// + /// Authenticate users one time passcode against the OASIS platform + /// + /// + /// + /// + public bool Authenticate(string username, string password, string clientIP = null) + { + var apiKey = ConfigurationManager.AppSettings["OASISApiKey"]; + var appKey = ConfigurationManager.AppSettings["OASISAppKey"]; + var appID = long.Parse(ConfigurationManager.AppSettings["OASISAppID"]); + + if (!string.IsNullOrEmpty(clientIP) && (clientIP.Equals("127.0.0.1") || clientIP.Equals("::1"))) + clientIP = null; + + var oasis = new OTPProvider(appID, appKey, apiKey, RemoteIP:clientIP); + + var state = oasis.RequestAuthorisationState(new RequestAuthorisationState + { + Username = username, + VerificationType = VerificationTypeEnum.LOGIN + }); + + if (state.State == UserAuthenticatorStateEnum.SKIPAUTHENTICATION) + return true; + + if (state.State != UserAuthenticatorStateEnum.AUTHENTICATE) + return false; + + var result = oasis.VerifyUserOTP(new VerifyUserOTP + { + Username = username, + OTPCode = password, + VerificationType = VerificationTypeEnum.LOGIN + }); + + return result.State == UserAuthenticatorStateEnum.VALID; + } + + public string PromptLabel + { + get + { + return "OASIS OTP Code"; + } + } + + public string ProviderURL + { + get + { + return "http://www.oliveinnovations.com"; + } + } + } +} \ No newline at end of file diff --git a/Myrtille.MFAProviders/Properties/AssemblyInfo.cs b/Myrtille.MFAProviders/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e5903bf --- /dev/null +++ b/Myrtille.MFAProviders/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Myrtille.MFAProviders")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Myrtille")] +[assembly: AssemblyCopyright("Copyright © Olive Innovations Ltd 2017-2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3573c84d-5fd3-4bac-bf96-20b9fd0709ff")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.8.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Myrtille.MFAProviders/packages.config b/Myrtille.MFAProviders/packages.config new file mode 100644 index 0000000..6daf69e --- /dev/null +++ b/Myrtille.MFAProviders/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Adapters/IEnterpriseAdapter.cs b/Myrtille.Services.Contracts/Adapters/IEnterpriseAdapter.cs new file mode 100644 index 0000000..e744fb5 --- /dev/null +++ b/Myrtille.Services.Contracts/Adapters/IEnterpriseAdapter.cs @@ -0,0 +1,46 @@ +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + Copyright(c) 2018 Paul Oliver + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Collections.Generic; + +namespace Myrtille.Services.Contracts +{ + public interface IEnterpriseAdapter + { + void Initialize(); + + EnterpriseSession Authenticate(string username, string password, string adminGroup, string domain); + + void Logout(string sessionID); + + long? AddHost(EnterpriseHostEdit editHost, string sessionID); + + EnterpriseHostEdit GetHost(long hostID, string sessionID); + + bool UpdateHost(EnterpriseHostEdit editHost, string sessionID); + + bool DeleteHost(long hostID, string sessionID); + + List SessionHosts(string sessionID); + + EnterpriseConnectionDetails GetSessionConnectionDetails(string sessionID, long hostID, string sessionKey); + + string CreateUserSession(string sessionID, long hostID, string username, string password); + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Adapters/IMultifactorAuthenticationAdapter.cs b/Myrtille.Services.Contracts/Adapters/IMultifactorAuthenticationAdapter.cs new file mode 100644 index 0000000..cac77b6 --- /dev/null +++ b/Myrtille.Services.Contracts/Adapters/IMultifactorAuthenticationAdapter.cs @@ -0,0 +1,42 @@ +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + Copyright(c) 2018 Paul Oliver + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +namespace Myrtille.Services.Contracts +{ + public interface IMultifactorAuthenticationAdapter + { + /// + /// Validate credentials using the adapter + /// + /// Username + /// Password + /// + bool Authenticate(string username, string password, string clientIP = null); + + /// + /// This is the label to be displayed on the login screen + /// + string PromptLabel { get; } + + /// + /// link to MFA provider page + /// + string ProviderURL { get; } + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/IEnterpriseService.cs b/Myrtille.Services.Contracts/IEnterpriseService.cs new file mode 100644 index 0000000..e5f1469 --- /dev/null +++ b/Myrtille.Services.Contracts/IEnterpriseService.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using System.ServiceModel; + +namespace Myrtille.Services.Contracts +{ + [ServiceContract] + public interface IEnterpriseService + { + [OperationContract] + bool GetState(); + + [OperationContract] + EnterpriseSession Authenticate(string username, string password); + + [OperationContract] + void Logout(string sessionID); + + [OperationContract] + long? AddHost(EnterpriseHostEdit editHost, string sessionID); + + [OperationContract] + EnterpriseHostEdit GetHost(long hostID, string sessionID); + + [OperationContract] + bool UpdateHost(EnterpriseHostEdit editHost, string sessionID); + + [OperationContract] + bool DeleteHost(long hostID, string sessionID); + + [OperationContract] + List GetSessionHosts(string sessionID); + + [OperationContract] + EnterpriseConnectionDetails GetSessionConnectionDetails(string sessionID, long hostID, string sessionKey); + + [OperationContract] + string CreateUserSession(string sessionID, long hostID, string username, string password); + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/IMFAAuthentication.cs b/Myrtille.Services.Contracts/IMFAAuthentication.cs new file mode 100644 index 0000000..57da2b7 --- /dev/null +++ b/Myrtille.Services.Contracts/IMFAAuthentication.cs @@ -0,0 +1,20 @@ +using System.ServiceModel; + +namespace Myrtille.Services.Contracts +{ + [ServiceContract] + public interface IMFAAuthentication + { + [OperationContract] + bool GetState(); + + [OperationContract] + bool Authenticate(string username, string password, string clientIP = null); + + [OperationContract] + string GetPromptLabel(); + + [OperationContract] + string GetProviderURL(); + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/IRemoteSessionProcess.cs b/Myrtille.Services.Contracts/IRemoteSessionProcess.cs index 7e59c6e..c031a39 100644 --- a/Myrtille.Services.Contracts/IRemoteSessionProcess.cs +++ b/Myrtille.Services.Contracts/IRemoteSessionProcess.cs @@ -29,8 +29,14 @@ public interface IRemoteSessionProcess [OperationContract] void StartProcess( int remoteSessionId, + string serverAddress, + string userDomain, + string userName, + string startProgram, int clientWidth, - int clientHeight); + int clientHeight, + bool allowRemoteClipboard, + SecurityProtocolEnum securityProtocol); /// /// stop the rdp client process diff --git a/Myrtille.Services.Contracts/Models/EnterpriseConnectionDetails.cs b/Myrtille.Services.Contracts/Models/EnterpriseConnectionDetails.cs new file mode 100644 index 0000000..49a7854 --- /dev/null +++ b/Myrtille.Services.Contracts/Models/EnterpriseConnectionDetails.cs @@ -0,0 +1,8 @@ +namespace Myrtille.Services.Contracts +{ + public class EnterpriseConnectionDetails : EnterpriseHost + { + public string Username { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Models/EnterpriseHost.cs b/Myrtille.Services.Contracts/Models/EnterpriseHost.cs new file mode 100644 index 0000000..5df0af0 --- /dev/null +++ b/Myrtille.Services.Contracts/Models/EnterpriseHost.cs @@ -0,0 +1,10 @@ +namespace Myrtille.Services.Contracts +{ + public class EnterpriseHost + { + public long HostID { get; set; } + public string HostName { get; set; } + public string HostAddress { get; set; } + public SecurityProtocolEnum Protocol { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Models/EnterpriseHostEdit.cs b/Myrtille.Services.Contracts/Models/EnterpriseHostEdit.cs new file mode 100644 index 0000000..7f4c920 --- /dev/null +++ b/Myrtille.Services.Contracts/Models/EnterpriseHostEdit.cs @@ -0,0 +1,11 @@ +namespace Myrtille.Services.Contracts +{ + public class EnterpriseHostEdit + { + public long HostID { get; set; } + public string HostName { get; set; } + public string HostAddress { get; set; } + public string DirectoryGroups { get; set; } + public SecurityProtocolEnum Protocol { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Models/EnterpriseSession.cs b/Myrtille.Services.Contracts/Models/EnterpriseSession.cs new file mode 100644 index 0000000..d795db1 --- /dev/null +++ b/Myrtille.Services.Contracts/Models/EnterpriseSession.cs @@ -0,0 +1,9 @@ +namespace Myrtille.Services.Contracts +{ + public class EnterpriseSession + { + public bool IsAdmin { get; set; } + public string SessionID { get; set; } + public string SessionKey { get; set; } + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Models/SecurityProtocolEnum.cs b/Myrtille.Services.Contracts/Models/SecurityProtocolEnum.cs new file mode 100644 index 0000000..0ba4f33 --- /dev/null +++ b/Myrtille.Services.Contracts/Models/SecurityProtocolEnum.cs @@ -0,0 +1,11 @@ +namespace Myrtille.Services.Contracts +{ + public enum SecurityProtocolEnum + { + auto = 0, + rdp = 1, + tls = 2, + nla = 3, + ext = 4 + } +} \ No newline at end of file diff --git a/Myrtille.Services.Contracts/Myrtille.Services.Contracts.csproj b/Myrtille.Services.Contracts/Myrtille.Services.Contracts.csproj index 70d2a75..e1cc9b1 100644 --- a/Myrtille.Services.Contracts/Myrtille.Services.Contracts.csproj +++ b/Myrtille.Services.Contracts/Myrtille.Services.Contracts.csproj @@ -66,8 +66,17 @@ + + + + + + + + + diff --git a/Myrtille.Services.Contracts/Properties/AssemblyInfo.cs b/Myrtille.Services.Contracts/Properties/AssemblyInfo.cs index 04cd75d..57db5f9 100644 --- a/Myrtille.Services.Contracts/Properties/AssemblyInfo.cs +++ b/Myrtille.Services.Contracts/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // Vous pouvez spécifier toutes les valeurs ou indiquer les numéros de build et de révision par défaut // en utilisant '*', comme indiqué ci-dessous : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.8.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Myrtille.Services/EnterpriseService.cs b/Myrtille.Services/EnterpriseService.cs new file mode 100644 index 0000000..f4639fb --- /dev/null +++ b/Myrtille.Services/EnterpriseService.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Myrtille.Services.Contracts; + +namespace Myrtille.Services +{ + public class EnterpriseService : IEnterpriseService + { + public bool GetState() + { + return Program._enterpriseAdapter != null; + } + + public EnterpriseSession Authenticate(string username, string password) + { + try + { + Trace.TraceInformation("Requesting authentication of user {0}", username); + return Program._enterpriseAdapter.Authenticate(username, password, Program._adminGroup, Program._enterpriseDomain); + } + catch (Exception ex) + { + Trace.TraceError("Failed to authenticate user {0}, ({1})", username, ex); + return null; + } + } + + public void Logout(string sessionID) + { + Program._enterpriseAdapter.Logout(sessionID); + } + + public long? AddHost(EnterpriseHostEdit editHost, string sessionID) + { + try + { + Trace.TraceInformation("Add host requested, host {0}", editHost.HostName); + return Program._enterpriseAdapter.AddHost(editHost, sessionID); + }catch(Exception ex) + { + Trace.TraceError("Failed to add host {0}, ({1})",editHost.HostName, ex); + return null; + } + } + + public EnterpriseHostEdit GetHost(long hostID, string sessionID) + { + try + { + Trace.TraceInformation("Edit host requested, host {0}", hostID); + return Program._enterpriseAdapter.GetHost(hostID, sessionID); + } + catch (Exception ex) + { + Trace.TraceError("Failed to get host {0}, ({1})", hostID, ex); + return null; + } + } + + public bool UpdateHost(EnterpriseHostEdit editHost, string sessionID) + { + try + { + Trace.TraceInformation("Update host requested, host {0}", editHost.HostName); + return Program._enterpriseAdapter.UpdateHost(editHost, sessionID); + } + catch (Exception ex) + { + Trace.TraceError("Failed to update host {0}, ({1})", editHost.HostName, ex); + return false; + } + } + + public bool DeleteHost(long hostID, string sessionID) + { + try + { + Trace.TraceInformation("Deleting host"); + return Program._enterpriseAdapter.DeleteHost(hostID, sessionID); + } + catch (Exception ex) + { + Trace.TraceError("Unable to delete host {0} ({1})", hostID, ex); + return false; + } + } + + public List GetSessionHosts(string sessionID) + { + try + { + Trace.TraceInformation("Requesting session host list"); + return Program._enterpriseAdapter.SessionHosts(sessionID); + } + catch (Exception ex) + { + Trace.TraceError("Unable to get host list {0}", ex); + return new List(); + } + } + + public EnterpriseConnectionDetails GetSessionConnectionDetails(string sessionID, long hostID, string sessionKey) + { + try + { + Trace.TraceInformation("Requesting session details"); + return Program._enterpriseAdapter.GetSessionConnectionDetails(sessionID, hostID, sessionKey); + } + catch(Exception ex) + { + Trace.TraceError("Unable to get session connection details {0}", ex); + return null; + } + } + + public string CreateUserSession(string sessionID, long hostID, string username, string password) + { + try + { + Trace.TraceInformation("Create user session requested, host {0}, user {1}", hostID, username); + return Program._enterpriseAdapter.CreateUserSession(sessionID,hostID,username,password); + } + catch (Exception ex) + { + Trace.TraceError("Failed to create session {0}, ({1})", hostID, ex); + return null; + } + } + } +} \ No newline at end of file diff --git a/Myrtille.Services/MFAAuthentication.cs b/Myrtille.Services/MFAAuthentication.cs new file mode 100644 index 0000000..62c4ffb --- /dev/null +++ b/Myrtille.Services/MFAAuthentication.cs @@ -0,0 +1,27 @@ +using Myrtille.Services.Contracts; + +namespace Myrtille.Services +{ + public class MFAAuthentication : IMFAAuthentication + { + public bool GetState() + { + return Program._multifactorAdapter != null; + } + + public bool Authenticate(string username, string password, string clientIP = null) + { + return Program._multifactorAdapter.Authenticate(username, password, clientIP); + } + + public string GetPromptLabel() + { + return Program._multifactorAdapter.PromptLabel; + } + + public string GetProviderURL() + { + return Program._multifactorAdapter.ProviderURL; + } + } +} \ No newline at end of file diff --git a/Myrtille.Services/Myrtille.Services.csproj b/Myrtille.Services/Myrtille.Services.csproj index c563c84..bb53eae 100644 --- a/Myrtille.Services/Myrtille.Services.csproj +++ b/Myrtille.Services/Myrtille.Services.csproj @@ -89,14 +89,31 @@ myrtille.ico + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll + True + + + ..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll + True + + + ..\packages\EntityFramework.SqlServerCompact.6.2.0\lib\net45\EntityFramework.SqlServerCompact.dll + True + ..\packages\log4net.2.0.8\lib\net45-full\log4net.dll True + + + ..\packages\Microsoft.SqlServer.Compact.4.0.8876.1\lib\net40\System.Data.SqlServerCe.dll + True + @@ -107,7 +124,9 @@ + + Component @@ -122,6 +141,7 @@ Designer + @@ -150,6 +170,14 @@ {37630774-1321-4E6A-8661-4430A8946E9E} Myrtille.Common + + {9e4bf7c3-8ea3-428c-bea3-06cc3afcaeb4} + Myrtille.Enterprise + + + {3573c84d-5fd3-4bac-bf96-20b9fd0709ff} + Myrtille.MFAProviders + {010E1702-3045-4B13-BFB6-06FFC60B5CBB} Myrtille.Services.Contracts @@ -159,6 +187,13 @@ + + + if not exist "$(TargetDir)x86" md "$(TargetDir)x86" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\x86\*.*" "$(TargetDir)x86" + if not exist "$(TargetDir)amd64" md "$(TargetDir)amd64" + xcopy /s /y "$(SolutionDir)packages\Microsoft.SqlServer.Compact.4.0.8876.1\NativeBinaries\amd64\*.*" "$(TargetDir)amd64" + - + @@ -36,6 +38,25 @@ + + + + + + + + + + + + + + + + + + + @@ -55,8 +76,24 @@ + + + + + + + + + + + + + + + + - + @@ -72,7 +109,7 @@ - + @@ -81,16 +118,56 @@ - + - + - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/Myrtille.Services/packages.config b/Myrtille.Services/packages.config index 9a0551c..99246db 100644 --- a/Myrtille.Services/packages.config +++ b/Myrtille.Services/packages.config @@ -1,4 +1,7 @@  + + + \ No newline at end of file diff --git a/Myrtille.Setup/Myrtille.Setup.vdproj b/Myrtille.Setup/Myrtille.Setup.vdproj index 672aeb1..17e3bbc 100644 --- a/Myrtille.Setup/Myrtille.Setup.vdproj +++ b/Myrtille.Setup/Myrtille.Setup.vdproj @@ -13,6 +13,12 @@ "SccProvider" = "8:" "Hierarchy" { + "Entry" + { + "MsmKey" = "8:_0B2BA4D8B5E04A3A80198F78FD3282B8" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } "Entry" { "MsmKey" = "8:_32098C275F7A4345B4341A599D4A17EA" @@ -21,14 +27,20 @@ } "Entry" { - "MsmKey" = "8:_493DA47191E40E875BBABCFF673D205D" + "MsmKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_493DA47191E40E875BBABCFF673D205D" - "OwnerKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" + "MsmKey" = "8:_3DEFEC1280CA50E9C6D144C206915989" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_44790C026B9A03A41098881622B115AE" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "MsmSig" = "8:_UNDEFINED" } "Entry" @@ -39,6 +51,12 @@ } "Entry" { + "MsmKey" = "8:_4ECBD3B5DDB248AC8B7040022826CC07" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_5177E07007F64650B52F96DCDAA8FA5F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -57,92 +75,284 @@ } "Entry" { + "MsmKey" = "8:_5BFCB2A90219431596BC42964CFDE7ED" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_62B8DAC75FFF49D797056A6BBBDBA085" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_65A7FD6885D9B6CDAA9B19EEC9724493" "OwnerKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" "MsmSig" = "8:_UNDEFINED" } "Entry" { + "MsmKey" = "8:_66F2CF0FC3964C14A69521899A2ED1FF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { + "MsmKey" = "8:_6FD268CAC27B464EBE683A085FEA49A3" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_709DF410E5BE4FC98F5ED11E43FE52A1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_715AF487B0E14F6293132F65F58BA908" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_715FDD5B6306473095F4C89873E3BBD4" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_80EEEEA7392F488BA634ACFB60BD797A" + "MsmKey" = "8:_7254D2026DF4439297E69472870E2713" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_8BF6850276284FB68DA9C10B9D29AB01" + "MsmKey" = "8:_738802CB54DC4C60A305D891F0078BA6" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" + "MsmKey" = "8:_78C32DEE808BD58AAE4F4CA16D993DEC" + "OwnerKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_78C32DEE808BD58AAE4F4CA16D993DEC" + "OwnerKey" = "8:_3DEFEC1280CA50E9C6D144C206915989" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_78C32DEE808BD58AAE4F4CA16D993DEC" + "OwnerKey" = "8:_44790C026B9A03A41098881622B115AE" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_78C32DEE808BD58AAE4F4CA16D993DEC" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_7EB427F5003C403ABD724E7222C110DF" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_BCC81D5C09CCB420520028C88D36985C" + "MsmKey" = "8:_801919A7D724D7D39851299D5040F57B" "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_BCC81D5C09CCB420520028C88D36985C" + "MsmKey" = "8:_801919A7D724D7D39851299D5040F57B" "OwnerKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_BCC81D5C09CCB420520028C88D36985C" - "OwnerKey" = "8:_493DA47191E40E875BBABCFF673D205D" + "MsmKey" = "8:_801919A7D724D7D39851299D5040F57B" + "OwnerKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_CDECD7EBA52548D3A554E2E2FCB726F7" + "MsmKey" = "8:_80EEEEA7392F488BA634ACFB60BD797A" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_CF9E122FB6B8483EAFCDCBF15F665BFC" + "MsmKey" = "8:_814036044F9AFF6D39FFD1B59AD844CF" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_83A15477F512314BC17E6D4FAEDDDF13" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_83A15477F512314BC17E6D4FAEDDDF13" + "OwnerKey" = "8:_B73B16074B9AC8898C95C06DFE180B1A" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8821D9D2862E40F9081C0069D035C9F8" + "OwnerKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8821D9D2862E40F9081C0069D035C9F8" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8821D9D2862E40F9081C0069D035C9F8" + "OwnerKey" = "8:_3DEFEC1280CA50E9C6D144C206915989" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_8ABBC04673824C6CAECD1383DA5346E9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_D0776132C5BE488094C78321361676CF" + "MsmKey" = "8:_8BF6850276284FB68DA9C10B9D29AB01" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_9186723DB4B94643BE0E7976C34CADE0" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_ABE4E421EB25401FA22DB71407D36135" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE0BA4806C1E4C3DB030DA3E5F6380E1" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE539CE462393EB4A3EB280F986B8160" + "OwnerKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE539CE462393EB4A3EB280F986B8160" + "OwnerKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE539CE462393EB4A3EB280F986B8160" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_AE539CE462393EB4A3EB280F986B8160" + "OwnerKey" = "8:_814036044F9AFF6D39FFD1B59AD844CF" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B73B16074B9AC8898C95C06DFE180B1A" + "OwnerKey" = "8:_814036044F9AFF6D39FFD1B59AD844CF" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_B73B16074B9AC8898C95C06DFE180B1A" + "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_BF505211616D4E9191B1B102DE7C37D9" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_D551AF0D5F6B9C133F2A1E9E4EC1B3AA" + "MsmKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "MsmSig" = "8:_UNDEFINED" } "Entry" { - "MsmKey" = "8:_D551AF0D5F6B9C133F2A1E9E4EC1B3AA" + "MsmKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" "OwnerKey" = "8:_AA7B9254EDD24A378250099DC28F1BC1" "MsmSig" = "8:_UNDEFINED" } "Entry" { + "MsmKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" + "OwnerKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_C41EB1C9257A4C089AC4EA108CA2929A" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CDECD7EBA52548D3A554E2E2FCB726F7" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_CF9E122FB6B8483EAFCDCBF15F665BFC" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_D0776132C5BE488094C78321361676CF" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_E497BBBCCE5862BA8BF99254BA4C5BC8" - "OwnerKey" = "8:_493DA47191E40E875BBABCFF673D205D" + "OwnerKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" "MsmSig" = "8:_UNDEFINED" } "Entry" @@ -165,6 +375,12 @@ } "Entry" { + "MsmKey" = "8:_E54B03E5E31F47E5BF1FCA6BED901337" + "OwnerKey" = "8:_UNDEFINED" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { "MsmKey" = "8:_EC775426DD3D46C6BBBF50696834BC7F" "OwnerKey" = "8:_UNDEFINED" "MsmSig" = "8:_UNDEFINED" @@ -178,7 +394,7 @@ "Entry" { "MsmKey" = "8:_UNDEFINED" - "OwnerKey" = "8:_D551AF0D5F6B9C133F2A1E9E4EC1B3AA" + "OwnerKey" = "8:_3DEFEC1280CA50E9C6D144C206915989" "MsmSig" = "8:_UNDEFINED" } "Entry" @@ -196,19 +412,61 @@ "Entry" { "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_44790C026B9A03A41098881622B115AE" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_B73B16074B9AC8898C95C06DFE180B1A" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_814036044F9AFF6D39FFD1B59AD844CF" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_8821D9D2862E40F9081C0069D035C9F8" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_78C32DEE808BD58AAE4F4CA16D993DEC" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_AE539CE462393EB4A3EB280F986B8160" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_34DD21D9DAC199D4628ADF6B75BB789C" + "MsmSig" = "8:_UNDEFINED" + } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" "OwnerKey" = "8:_E497BBBCCE5862BA8BF99254BA4C5BC8" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" - "OwnerKey" = "8:_493DA47191E40E875BBABCFF673D205D" + "OwnerKey" = "8:_BFE706B30885C685BDC23926EDA3D2F1" "MsmSig" = "8:_UNDEFINED" } "Entry" { "MsmKey" = "8:_UNDEFINED" - "OwnerKey" = "8:_BCC81D5C09CCB420520028C88D36985C" + "OwnerKey" = "8:_801919A7D724D7D39851299D5040F57B" "MsmSig" = "8:_UNDEFINED" } "Entry" @@ -217,6 +475,12 @@ "OwnerKey" = "8:_6A00A4B5E0C64F48AF08EC529C41D062" "MsmSig" = "8:_UNDEFINED" } + "Entry" + { + "MsmKey" = "8:_UNDEFINED" + "OwnerKey" = "8:_83A15477F512314BC17E6D4FAEDDDF13" + "MsmSig" = "8:_UNDEFINED" + } } "Configurations" { @@ -244,15 +508,10 @@ "ComponentsUrl" = "8:" "Items" { - "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.5" + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.5.2" { - "Name" = "8:Microsoft .NET Framework 4.5 (x86 and x64)" - "ProductCode" = "8:.NETFramework,Version=v4.5" - } - "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.4.5" - { - "Name" = "8:Windows Installer 4.5" - "ProductCode" = "8:Microsoft.Windows.Installer.4.5" + "Name" = "8:Microsoft .NET Framework 4.5.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.5.2" } } } @@ -281,15 +540,10 @@ "ComponentsUrl" = "8:" "Items" { - "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.5" - { - "Name" = "8:Microsoft .NET Framework 4.5 (x86 and x64)" - "ProductCode" = "8:.NETFramework,Version=v4.5" - } - "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:Microsoft.Windows.Installer.4.5" + "{EDC2488A-8267-493A-A98E-7D9C3B36CDF3}:.NETFramework,Version=v4.5.2" { - "Name" = "8:Windows Installer 4.5" - "ProductCode" = "8:Microsoft.Windows.Installer.4.5" + "Name" = "8:Microsoft .NET Framework 4.5.2 (x86 and x64)" + "ProductCode" = "8:.NETFramework,Version=v4.5.2" } } } @@ -434,12 +688,436 @@ } "File" { + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_0B2BA4D8B5E04A3A80198F78FD3282B8" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\Microsoft.VC90.CRT\\Microsoft.VC90.CRT.manifest" + "TargetName" = "8:Microsoft.VC90.CRT.manifest" + "Tag" = "8:" + "Folder" = "8:_889006B3BB5F432D87C15DA9EF3ABE1D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_32098C275F7A4345B4341A599D4A17EA" { "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\libeay32.dll" "TargetName" = "8:libeay32.dll" "Tag" = "8:" - "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_34DD21D9DAC199D4628ADF6B75BB789C" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Myrtille.Enterprise, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_34DD21D9DAC199D4628ADF6B75BB789C" + { + "Name" = "8:Myrtille.Enterprise.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:Myrtille.Enterprise.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_3DEFEC1280CA50E9C6D144C206915989" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:EntityFramework.SqlServerCompact, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_3DEFEC1280CA50E9C6D144C206915989" + { + "Name" = "8:EntityFramework.SqlServerCompact.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:EntityFramework.SqlServerCompact.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_44790C026B9A03A41098881622B115AE" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_44790C026B9A03A41098881622B115AE" + { + "Name" = "8:EntityFramework.SqlServer.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:EntityFramework.SqlServer.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4C1C75940E694E2D986E018CFB89863C" + { + "SourcePath" = "8:..\\DOCUMENTATION.md" + "TargetName" = "8:DOCUMENTATION.md" + "Tag" = "8:" + "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4ECBD3B5DDB248AC8B7040022826CC07" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\Microsoft.VC90.CRT\\Microsoft.VC90.CRT.manifest" + "TargetName" = "8:Microsoft.VC90.CRT.manifest" + "Tag" = "8:" + "Folder" = "8:_5E856618918C430F949EF508F2AB403D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5177E07007F64650B52F96DCDAA8FA5F" + { + "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\freerdp2.dll" + "TargetName" = "8:freerdp2.dll" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_552CC8B4B41643C6A5AB4886DB52BD40" + { + "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\ssleay32.dll" + "TargetName" = "8:ssleay32.dll" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5B485B9AA1224F56A609636F984E70EC" + { + "SourcePath" = "8:..\\CHANGELOG" + "TargetName" = "8:CHANGELOG" + "Tag" = "8:" + "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5BFCB2A90219431596BC42964CFDE7ED" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlceqp40.dll" + "TargetName" = "8:sqlceqp40.dll" + "Tag" = "8:" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_62B8DAC75FFF49D797056A6BBBDBA085" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlceqp40.dll" + "TargetName" = "8:sqlceqp40.dll" + "Tag" = "8:" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_65A7FD6885D9B6CDAA9B19EEC9724493" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Microsoft.WebSockets, Version=0.2.3.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_65A7FD6885D9B6CDAA9B19EEC9724493" + { + "Name" = "8:Microsoft.WebSockets.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:Microsoft.WebSockets.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_66F2CF0FC3964C14A69521899A2ED1FF" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlcecompact40.dll" + "TargetName" = "8:sqlcecompact40.dll" + "Tag" = "8:" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_6FD268CAC27B464EBE683A085FEA49A3" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlcecompact40.dll" + "TargetName" = "8:sqlcecompact40.dll" + "Tag" = "8:" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_709DF410E5BE4FC98F5ED11E43FE52A1" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlceca40.dll" + "TargetName" = "8:sqlceca40.dll" + "Tag" = "8:" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_715AF487B0E14F6293132F65F58BA908" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\Microsoft.VC90.CRT\\README_ENU.txt" + "TargetName" = "8:README_ENU.txt" + "Tag" = "8:" + "Folder" = "8:_5E856618918C430F949EF508F2AB403D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_715FDD5B6306473095F4C89873E3BBD4" + { + "SourcePath" = "8:..\\README.md" + "TargetName" = "8:README.md" + "Tag" = "8:" + "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7254D2026DF4439297E69472870E2713" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlceme40.dll" + "TargetName" = "8:sqlceme40.dll" + "Tag" = "8:" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_738802CB54DC4C60A305D891F0078BA6" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlcese40.dll" + "TargetName" = "8:sqlcese40.dll" + "Tag" = "8:" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -454,20 +1132,20 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_493DA47191E40E875BBABCFF673D205D" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_78C32DEE808BD58AAE4F4CA16D993DEC" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" - "AssemblyAsmDisplayName" = "8:Myrtille.Common, Version=1.6.1.0, Culture=neutral, processorArchitecture=MSIL" + "AssemblyAsmDisplayName" = "8:EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL" "ScatterAssemblies" { - "_493DA47191E40E875BBABCFF673D205D" + "_78C32DEE808BD58AAE4F4CA16D993DEC" { - "Name" = "8:Myrtille.Common.dll" + "Name" = "8:EntityFramework.dll" "Attributes" = "3:512" } } - "SourcePath" = "8:Myrtille.Common.dll" + "SourcePath" = "8:EntityFramework.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" @@ -485,12 +1163,12 @@ "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_4C1C75940E694E2D986E018CFB89863C" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_7EB427F5003C403ABD724E7222C110DF" { - "SourcePath" = "8:..\\DOCUMENTATION.md" - "TargetName" = "8:DOCUMENTATION.md" + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlceer40EN.dll" + "TargetName" = "8:sqlceer40EN.dll" "Tag" = "8:" - "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -505,10 +1183,21 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5177E07007F64650B52F96DCDAA8FA5F" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_801919A7D724D7D39851299D5040F57B" { - "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\freerdp2.dll" - "TargetName" = "8:freerdp2.dll" + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_801919A7D724D7D39851299D5040F57B" + { + "Name" = "8:Microsoft.Web.Administration.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:Microsoft.Web.Administration.dll" + "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" "Condition" = "8:" @@ -522,13 +1211,13 @@ "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" - "IsDependency" = "11:FALSE" + "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_552CC8B4B41643C6A5AB4886DB52BD40" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_80EEEEA7392F488BA634ACFB60BD797A" { - "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\ssleay32.dll" - "TargetName" = "8:ssleay32.dll" + "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\winpr-tools2.dll" + "TargetName" = "8:winpr-tools2.dll" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" "Condition" = "8:" @@ -545,12 +1234,23 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_5B485B9AA1224F56A609636F984E70EC" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_814036044F9AFF6D39FFD1B59AD844CF" { - "SourcePath" = "8:..\\CHANGELOG" - "TargetName" = "8:CHANGELOG" + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Myrtille.MFAProviders, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_814036044F9AFF6D39FFD1B59AD844CF" + { + "Name" = "8:Myrtille.MFAProviders.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:Myrtille.MFAProviders.dll" + "TargetName" = "8:" "Tag" = "8:" - "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -562,23 +1262,23 @@ "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" - "IsDependency" = "11:FALSE" + "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_65A7FD6885D9B6CDAA9B19EEC9724493" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_83A15477F512314BC17E6D4FAEDDDF13" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" - "AssemblyAsmDisplayName" = "8:Microsoft.WebSockets, Version=0.2.3.0, Culture=neutral, processorArchitecture=MSIL" + "AssemblyAsmDisplayName" = "8:System.Net.Http, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" "ScatterAssemblies" { - "_65A7FD6885D9B6CDAA9B19EEC9724493" + "_83A15477F512314BC17E6D4FAEDDDF13" { - "Name" = "8:Microsoft.WebSockets.dll" + "Name" = "8:System.Net.Http.dll" "Attributes" = "3:512" } } - "SourcePath" = "8:Microsoft.WebSockets.dll" + "SourcePath" = "8:System.Net.Http.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" @@ -596,12 +1296,23 @@ "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_715FDD5B6306473095F4C89873E3BBD4" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_8821D9D2862E40F9081C0069D035C9F8" { - "SourcePath" = "8:..\\README.md" - "TargetName" = "8:README.md" + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:System.Data.SqlServerCe, Version=4.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_8821D9D2862E40F9081C0069D035C9F8" + { + "Name" = "8:System.Data.SqlServerCe.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:System.Data.SqlServerCe.dll" + "TargetName" = "8:" "Tag" = "8:" - "Folder" = "8:_B8E8F5F7726243109EBB878F4275F26E" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -613,15 +1324,15 @@ "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" - "IsDependency" = "11:FALSE" + "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_80EEEEA7392F488BA634ACFB60BD797A" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_8ABBC04673824C6CAECD1383DA5346E9" { - "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\winpr-tools2.dll" - "TargetName" = "8:winpr-tools2.dll" + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlceer40EN.dll" + "TargetName" = "8:sqlceer40EN.dll" "Tag" = "8:" - "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -656,20 +1367,162 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_BCC81D5C09CCB420520028C88D36985C" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_9186723DB4B94643BE0E7976C34CADE0" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\Microsoft.VC90.CRT\\msvcr90.dll" + "TargetName" = "8:msvcr90.dll" + "Tag" = "8:" + "Folder" = "8:_5E856618918C430F949EF508F2AB403D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_ABE4E421EB25401FA22DB71407D36135" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlcese40.dll" + "TargetName" = "8:sqlcese40.dll" + "Tag" = "8:" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_AE0BA4806C1E4C3DB030DA3E5F6380E1" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\Microsoft.VC90.CRT\\README_ENU.txt" + "TargetName" = "8:README_ENU.txt" + "Tag" = "8:" + "Folder" = "8:_889006B3BB5F432D87C15DA9EF3ABE1D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_AE539CE462393EB4A3EB280F986B8160" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" - "AssemblyAsmDisplayName" = "8:Microsoft.Web.Administration, Version=7.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" + "AssemblyAsmDisplayName" = "8:Myrtille.Services.Contracts, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL" "ScatterAssemblies" { - "_BCC81D5C09CCB420520028C88D36985C" + "_AE539CE462393EB4A3EB280F986B8160" { - "Name" = "8:Microsoft.Web.Administration.dll" + "Name" = "8:Myrtille.Services.Contracts.dll" "Attributes" = "3:512" } } - "SourcePath" = "8:Microsoft.Web.Administration.dll" + "SourcePath" = "8:Myrtille.Services.Contracts.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_B73B16074B9AC8898C95C06DFE180B1A" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:OASIS.Integration, Version=1.6.1.0, Culture=neutral, PublicKeyToken=0c18c0483c9815ce, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_B73B16074B9AC8898C95C06DFE180B1A" + { + "Name" = "8:OASIS.Integration.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:OASIS.Integration.dll" + "TargetName" = "8:" + "Tag" = "8:" + "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:TRUE" + "IsolateTo" = "8:" + } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_BF505211616D4E9191B1B102DE7C37D9" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\sqlceca40.dll" + "TargetName" = "8:sqlceca40.dll" + "Tag" = "8:" + "Folder" = "8:_365BE1E5F7B842AFA09E271D4575F53D" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_BFE706B30885C685BDC23926EDA3D2F1" + { + "AssemblyRegister" = "3:1" + "AssemblyIsInGAC" = "11:FALSE" + "AssemblyAsmDisplayName" = "8:Myrtille.Common, Version=1.8.0.0, Culture=neutral, processorArchitecture=MSIL" + "ScatterAssemblies" + { + "_BFE706B30885C685BDC23926EDA3D2F1" + { + "Name" = "8:Myrtille.Common.dll" + "Attributes" = "3:512" + } + } + "SourcePath" = "8:Myrtille.Common.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" @@ -687,6 +1540,26 @@ "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_C41EB1C9257A4C089AC4EA108CA2929A" + { + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\x86\\sqlceme40.dll" + "TargetName" = "8:sqlceme40.dll" + "Tag" = "8:" + "Folder" = "8:_3610012282CA47DDAC6B705C3FA844DB" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Vital" = "11:TRUE" + "ReadOnly" = "11:FALSE" + "Hidden" = "11:FALSE" + "System" = "11:FALSE" + "Permanent" = "11:FALSE" + "SharedLegacy" = "11:FALSE" + "PackageAs" = "3:1" + "Register" = "3:1" + "Exclude" = "11:FALSE" + "IsDependency" = "11:FALSE" + "IsolateTo" = "8:" + } "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_CDECD7EBA52548D3A554E2E2FCB726F7" { "SourcePath" = "8:RDPSetup.reg" @@ -747,20 +1620,20 @@ "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_D551AF0D5F6B9C133F2A1E9E4EC1B3AA" + "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_E497BBBCCE5862BA8BF99254BA4C5BC8" { "AssemblyRegister" = "3:1" "AssemblyIsInGAC" = "11:FALSE" - "AssemblyAsmDisplayName" = "8:Myrtille.Services.Contracts, Version=1.6.1.0, Culture=neutral, processorArchitecture=MSIL" + "AssemblyAsmDisplayName" = "8:log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL" "ScatterAssemblies" { - "_D551AF0D5F6B9C133F2A1E9E4EC1B3AA" + "_E497BBBCCE5862BA8BF99254BA4C5BC8" { - "Name" = "8:Myrtille.Services.Contracts.dll" + "Name" = "8:log4net.dll" "Attributes" = "3:512" } } - "SourcePath" = "8:Myrtille.Services.Contracts.dll" + "SourcePath" = "8:log4net.dll" "TargetName" = "8:" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" @@ -778,21 +1651,10 @@ "IsDependency" = "11:TRUE" "IsolateTo" = "8:" } - "{9F6F8455-1EF1-4B85-886A-4223BCC8E7F7}:_E497BBBCCE5862BA8BF99254BA4C5BC8" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4EFA446B2E04EA48090783CC34DDBD0" { - "AssemblyRegister" = "3:1" - "AssemblyIsInGAC" = "11:FALSE" - "AssemblyAsmDisplayName" = "8:log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL" - "ScatterAssemblies" - { - "_E497BBBCCE5862BA8BF99254BA4C5BC8" - { - "Name" = "8:log4net.dll" - "Attributes" = "3:512" - } - } - "SourcePath" = "8:log4net.dll" - "TargetName" = "8:" + "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\winpr2.dll" + "TargetName" = "8:winpr2.dll" "Tag" = "8:" "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" "Condition" = "8:" @@ -806,15 +1668,15 @@ "PackageAs" = "3:1" "Register" = "3:1" "Exclude" = "11:FALSE" - "IsDependency" = "11:TRUE" + "IsDependency" = "11:FALSE" "IsolateTo" = "8:" } - "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E4EFA446B2E04EA48090783CC34DDBD0" + "{1FB2D0AE-D3B9-43D4-B9DD-F88EC61E35DE}:_E54B03E5E31F47E5BF1FCA6BED901337" { - "SourcePath" = "8:..\\Myrtille.RDP\\FreeRDP\\Release\\winpr2.dll" - "TargetName" = "8:winpr2.dll" + "SourcePath" = "8:..\\packages\\Microsoft.SqlServer.Compact.4.0.8876.1\\NativeBinaries\\amd64\\Microsoft.VC90.CRT\\msvcr90.dll" + "TargetName" = "8:msvcr90.dll" "Tag" = "8:" - "Folder" = "8:_FA146EB9AE2B44E2AB4B01E26999CAB2" + "Folder" = "8:_889006B3BB5F432D87C15DA9EF3ABE1D" "Condition" = "8:" "Transitive" = "11:FALSE" "Vital" = "11:TRUE" @@ -905,6 +1767,50 @@ "Property" = "8:_026245DB291B40CBA7011A81055B1681" "Folders" { + "{9EF0B969-E518-4E46-987F-47570745A589}:_3610012282CA47DDAC6B705C3FA844DB" + { + "Name" = "8:x86" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_AE8580512A804CD48F64FDE17762C960" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_5E856618918C430F949EF508F2AB403D" + { + "Name" = "8:Microsoft.VC90.CRT" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_56E61466CFF044688A29BD1197EFE32D" + "Folders" + { + } + } + } + } + "{9EF0B969-E518-4E46-987F-47570745A589}:_365BE1E5F7B842AFA09E271D4575F53D" + { + "Name" = "8:amd64" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_BADBB2AC29CA44D8BE3966C452305E0C" + "Folders" + { + "{9EF0B969-E518-4E46-987F-47570745A589}:_889006B3BB5F432D87C15DA9EF3ABE1D" + { + "Name" = "8:Microsoft.VC90.CRT" + "AlwaysCreate" = "11:FALSE" + "Condition" = "8:" + "Transitive" = "11:FALSE" + "Property" = "8:_EBAA513A91AD4BCB9A867044477DB8F8" + "Folders" + { + } + } + } + } } } } @@ -944,14 +1850,14 @@ "Name" = "8:Microsoft Visual Studio" "ProductName" = "8:Myrtille" "ProductCode" = "8:{B00BCC6F-3958-4726-9D99-DBAC78513C26}" - "PackageCode" = "8:{57F9CAC0-8B68-46FC-BBEC-BEF98790847D}" + "PackageCode" = "8:{8DD0DE88-8C5B-42C5-87FC-789929FC521D}" "UpgradeCode" = "8:{86A6145E-11DB-4EE7-9CE0-53F997050716}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" "DetectNewerInstalledVersion" = "11:TRUE" "InstallAllUsers" = "11:TRUE" - "ProductVersion" = "8:1.7.0" + "ProductVersion" = "8:1.8.0" "Manufacturer" = "8:Cedric Coste" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:http://cedrozor.github.io/myrtille" @@ -1226,7 +2132,7 @@ "ContextData" = "8:" "Attributes" = "3:0" "Setting" = "3:2" - "Value" = "8:The installer will guide you through the steps required to install [ProductName] [ProductVersion] on your computer.\n\nPlease ensure IIS and ASP.NET 4.x are enabled on this machine before continuing. Optionaly, if you have IIS 8 or greater, you can enable the websocket protocol into IIS for optimal performance." + "Value" = "8:The installer will guide you through the steps required to install [ProductName] [ProductVersion] on your computer.\n\nPlease ensure IIS and ASP.NET 4.5 are enabled on this machine before continuing. Optionaly, if you have IIS 8 or greater, you can enable the websocket protocol into IIS for optimal performance." "DefaultValue" = "8:#1203" "UsePlugInResources" = "11:TRUE" } diff --git a/Myrtille.Web/Default.aspx b/Myrtille.Web/Default.aspx index 1930f23..df85fbe 100644 --- a/Myrtille.Web/Default.aspx +++ b/Myrtille.Web/Default.aspx @@ -69,67 +69,114 @@
+ + + + -
+
- -
- - -
+ +
+ + +
+ + +
+ + +
+ + +
- -
- -
- +
- +
+ +
+ + +
+
- +
- - - - - +
- + myrtille <%=typeof(Default).Assembly.GetName().Version%>
+ + +
+ +
+ + + + +
+ +
+ + + + + + + +
+ + + + +
+ + host + +
+ +
+
+
+ +
+ diff --git a/Myrtille.Web/Default.aspx.cs b/Myrtille.Web/Default.aspx.cs index fdbcb8f..87fa5a8 100644 --- a/Myrtille.Web/Default.aspx.cs +++ b/Myrtille.Web/Default.aspx.cs @@ -18,22 +18,44 @@ limitations under the License. using System; using System.Collections.Generic; +using System.Configuration; using System.IO; using System.Text; using System.Text.RegularExpressions; +using System.Threading; using System.Web; using System.Web.SessionState; using System.Web.UI; +using System.Web.UI.HtmlControls; +using System.Web.UI.WebControls; using Myrtille.Helpers; +using Myrtille.Services.Contracts; namespace Myrtille.Web { public partial class Default : Page { + private MFAAuthenticationClient _mfaAuthClient; + private EnterpriseServiceClient _enterpriseClient; + + private EnterpriseSession _enterpriseSession; protected RemoteSession RemoteSession; /// - /// initialization + /// page init + /// + /// + /// + protected void Page_Init( + object sender, + EventArgs e) + { + _mfaAuthClient = new MFAAuthenticationClient(); + _enterpriseClient = new EnterpriseServiceClient(); + } + + /// + /// page load (postback data is now available) /// /// /// @@ -41,72 +63,121 @@ protected void Page_Load( object sender, EventArgs e) { - #region session fixation attack + // prevent session fixation or stealing + SessionFixationHandler(); - // prevent session fixation attack by generating a new session ID upon login - // https://www.owasp.org/index.php/Session_Fixation - if (!string.IsNullOrEmpty(HttpContext.Current.Request["oldSID"])) + // retrieve the active enterprise session, if any + if (HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) { try { - HttpContext.Current.Application.Lock(); - - // retrieve the given (old) http session - var httpSessions = (IDictionary)HttpContext.Current.Application[HttpApplicationStateVariables.HttpSessions.ToString()]; - var httpSession = httpSessions[HttpContext.Current.Request["oldSID"]]; - - // retrieve the remote session bound to it - var remoteSession = httpSession[HttpSessionStateVariables.RemoteSession.ToString()]; + _enterpriseSession = (EnterpriseSession)HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve the enterprise session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); + } + } - // unbind it from the old http session - httpSession[HttpSessionStateVariables.RemoteSession.ToString()] = null; + // retrieve the active remote session, if any + if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) + { + try + { + RemoteSession = (RemoteSession)HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()]; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve the remote session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); + } + } - // bind it to the new http session - HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] = remoteSession; + // postback events may redirect after execution; UI is updated from there + if (!IsPostBack) + { + UpdateControls(); + } - // cancel the old http session - httpSession.Abandon(); + // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests + Response.Cache.SetCacheability(HttpCacheability.NoCache); + Response.Cache.SetNoStore(); + } - // unregister it at application level - httpSessions.Remove(httpSession.SessionID); + /// + /// prevent http session fixation attack or stealing by generating a new http session ID upon login + /// https://www.owasp.org/index.php/Session_Fixation + /// + private void SessionFixationHandler() + { + // register the current http session + if (string.IsNullOrEmpty(HttpContext.Current.Request["oldSID"])) + { + try + { + HttpContext.Current.Application.Lock(); + var httpSessions = (IDictionary)HttpContext.Current.Application[HttpApplicationStateVariables.HttpSessions.ToString()]; + httpSessions[HttpContext.Current.Session.SessionID] = HttpContext.Current.Session; } catch (Exception exc) { - System.Diagnostics.Trace.TraceError("Failed to generate a new http session upon login ({0})", exc); + System.Diagnostics.Trace.TraceError("Failed to register http session ({0})", exc); } finally { HttpContext.Current.Application.UnLock(); } } - - #endregion - - try + // generate a new http session + else { - // retrieve the active remote session, if any - if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) + try { - try + HttpContext.Current.Application.Lock(); + + // retrieve the old http session id from url + var httpSessions = (IDictionary)HttpContext.Current.Application[HttpApplicationStateVariables.HttpSessions.ToString()]; + var httpSession = httpSessions[HttpContext.Current.Request["oldSID"]]; + + // retrieve the enterprise session bound to the old http session, if any + var enterpriseSession = httpSession[HttpSessionStateVariables.EnterpriseSession.ToString()]; + if (enterpriseSession != null) { - RemoteSession = (RemoteSession)HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()]; + // unbind from the old http session + httpSession[HttpSessionStateVariables.EnterpriseSession.ToString()] = null; + + // bind to the new http session + HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = enterpriseSession; } - catch (Exception exc) + + // retrieve the remote session bound to the old http session, if any + var remoteSession = httpSession[HttpSessionStateVariables.RemoteSession.ToString()]; + if (remoteSession != null) { - System.Diagnostics.Trace.TraceError("Failed to retrieve the remote session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); + // unbind from the old http session + httpSession[HttpSessionStateVariables.RemoteSession.ToString()] = null; + + // bind to the new http session + HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] = remoteSession; } - } - // update controls - UpdateControls(); + // unregister the old http session + httpSessions.Remove(httpSession.SessionID); - // disable the browser cache; in addition to a "noCache" dummy param, with current time, on long-polling and xhr requests - Response.Cache.SetCacheability(HttpCacheability.NoCache); - Response.Cache.SetNoStore(); - } - catch (Exception exc) - { - System.Diagnostics.Trace.TraceError("Failed to load myrtille ({0})", exc); + // remove the old http session id from url + Response.Redirect("?", true); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to generate a new http session upon login ({0})", exc); + } + finally + { + HttpContext.Current.Application.UnLock(); + } } } @@ -132,57 +203,215 @@ protected override void Render( ///
private void UpdateControls() { - if (RemoteSession != null) + // hosts list + if (_enterpriseSession != null && (RemoteSession == null || RemoteSession.State == RemoteSessionState.Disconnecting || RemoteSession.State == RemoteSessionState.Disconnected)) { - // login screen - loginScreen.Visible = RemoteSession.State != RemoteSessionState.Connecting && RemoteSession.State != RemoteSessionState.Connected; - - // toolbar - toolbar.Style["visibility"] = loginScreen.Visible ? "hidden" : "visible"; - toolbar.Style["display"] = loginScreen.Visible ? "none" : "block"; + toolbar.Style["visibility"] = "hidden"; + toolbar.Style["display"] = "none"; + hosts.Visible = true; + newHost.Visible = _enterpriseSession.IsAdmin; + hostsList.DataSource = _enterpriseClient.GetSessionHosts(_enterpriseSession.SessionID); + hostsList.DataBind(); + } + // active remote session + else if (RemoteSession != null && (RemoteSession.State == RemoteSessionState.Connecting || RemoteSession.State == RemoteSessionState.Connected)) + { + toolbar.Style["visibility"] = "visible"; + toolbar.Style["display"] = "block"; serverInfo.Value = RemoteSession.ServerAddress; stat.Value = RemoteSession.StatMode ? "Hide Stat" : "Show Stat"; - stat.Disabled = loginScreen.Visible; + stat.Disabled = false; debug.Value = RemoteSession.DebugMode ? "Hide Debug" : "Show Debug"; - debug.Disabled = loginScreen.Visible; + debug.Disabled = false; browser.Value = RemoteSession.CompatibilityMode ? "HTML5" : "HTML4"; - browser.Disabled = loginScreen.Visible; + browser.Disabled = false; scale.Value = RemoteSession.ScaleDisplay ? "Unscale" : "Scale"; - scale.Disabled = loginScreen.Visible; - keyboard.Disabled = loginScreen.Visible; - clipboard.Disabled = loginScreen.Visible; - files.Disabled = loginScreen.Visible || (RemoteSession.ServerAddress.ToLower() != "localhost" && RemoteSession.ServerAddress != "127.0.0.1" && RemoteSession.ServerAddress != HttpContext.Current.Request.Url.Host && string.IsNullOrEmpty(RemoteSession.UserDomain)) || string.IsNullOrEmpty(RemoteSession.UserName) || string.IsNullOrEmpty(RemoteSession.UserPassword); - cad.Disabled = loginScreen.Visible; - mrc.Disabled = loginScreen.Visible; - disconnect.Disabled = loginScreen.Visible; + scale.Disabled = false; + keyboard.Disabled = false; + clipboard.Disabled = !RemoteSession.AllowRemoteClipboard; + files.Disabled = RemoteSession.ServerAddress.ToLower() != "localhost" && RemoteSession.ServerAddress != "127.0.0.1" && RemoteSession.ServerAddress != "[::1]" && RemoteSession.ServerAddress != HttpContext.Current.Request.Url.Host && string.IsNullOrEmpty(RemoteSession.UserDomain); + cad.Disabled = false; + mrc.Disabled = false; + disconnect.Disabled = false; + } + // login screen + else + { + login.Visible = true; + + // MFA + if (_mfaAuthClient.GetState()) + { + mfaDiv.Visible = true; + mfaProvider.InnerText = _mfaAuthClient.GetPromptLabel(); + mfaProvider.HRef = _mfaAuthClient.GetProviderURL(); + } + + // enterprise mode + if (_enterpriseClient.GetState()) + { + domainServerDiv.Visible = false; + } + // standard mode + else + { + connect.Attributes["onclick"] = "showToolbar();"; + } } } /// - /// start the rdp session + /// enterprise mode from url: load the enterprise session (from querystring param) and proceed to connection; the user is non admin and the url is only usable once + /// enterprise mode from login: authenticate the user against the enterprise active directory and list the servers available to the user; the user is admin if member of the "EnterpriseAdminGroup" defined into myrtille services config + /// standard mode: connect the specified server; the rdp authentication is delegated to the rdp server or connection broker (if applicable) + /// if MFA is enabled and not already processed, authenticate the user against the configured MFA provider (OTP preferred) /// /// /// protected void ConnectButtonClick( - object sender, - EventArgs e) + object sender, + EventArgs e) { - // remove the active remote session, if any (disconnected?) - if (RemoteSession != null) + // one time usage enterprise session url + if (Request["SI"] != null && Request["SD"] != null && Request["SK"] != null) { - try + CreateEnterpriseSessionFromUrl(); + } + + // MFA (OTP passcode) + if (_enterpriseSession == null && _mfaAuthClient.GetState()) + { + var clientIP = ClientIPHelper.ClientIPFromRequest(new HttpContextWrapper(HttpContext.Current).Request, true, new string[] { }); + if (!_mfaAuthClient.Authenticate(user.Value, mfaPassword.Value, clientIP)) { - // unset the remote session for the current http session - HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] = null; + connectError.InnerText = "MFA Authentication failed!"; + UpdateControls(); + return; } - catch (Exception exc) + } + + // enterprise mode from login + if (_enterpriseSession == null && _enterpriseClient.GetState()) + { + CreateEnterpriseSessionFromLogin(); + } + // connection from: + // > standard mode + // > enterprise mode: hosts list + // > enterprise mode: one time session url + else + { + // the display size is required to start a remote session + // if missing, the client will provide it automatically + if (string.IsNullOrEmpty(width.Value) || string.IsNullOrEmpty(height.Value)) { - System.Diagnostics.Trace.TraceError("Failed to remove remote session ({0})", exc); + return; } - finally + + // connect + if (ConnectRemoteServer()) { - RemoteSession = null; + try + { + // in enterprise mode from login, a new http session was already generated (no need to do it each time an host is connected!) + // in standard mode or enterprise mode from url, a new http session must be generated + if (_enterpriseSession == null || Request["SI"] != null) + { + // cancel the current http session + HttpContext.Current.Session.Abandon(); + + // prevent session fixation attack by generating a new session ID upon login + // also, using http get method to prevent the browser asking for http post data confirmation if the page is reloaded + // https://www.owasp.org/index.php/Session_Fixation + Response.Redirect(string.Format("?oldSID={0}", HttpContext.Current.Session.SessionID), true); + } + // remove the host id from url + else + { + Response.Redirect("?", true); + } + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } } + // connection failed from the hosts list or from a one time session url + else if (_enterpriseSession != null && Request["SD"] != null) + { + try + { + // remove the host id from url + Response.Redirect("?", true); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } + } + } + } + + /// + /// connect the rdp server + /// + /// + /// the rdp authentication is delegated to the rdp server or connection broker (if applicable) + /// + private bool ConnectRemoteServer() + { + // connection parameters + var loginServer = string.IsNullOrEmpty(server.Value) ? "localhost" : server.Value; + var loginDomain = domain.Value; + var loginUser = user.Value; + var loginPassword = string.IsNullOrEmpty(passwordHash.Value) ? password.Value : RDPCryptoHelper.DecryptPassword(passwordHash.Value); + var loginProtocol = SecurityProtocolEnum.auto; + + // connect an host from the hosts list or from a one time session url + if (_enterpriseSession != null && Request["SD"] != null) + { + long hostId = 0; + if (long.TryParse(Request["SD"], out long lResult)) + { + hostId = lResult; + } + + try + { + // retrieve the host connection details + var connection = _enterpriseClient.GetSessionConnectionDetails(_enterpriseSession.SessionID, hostId, _enterpriseSession.SessionKey); + if (connection == null) + { + System.Diagnostics.Trace.TraceInformation("Unable to retrieve host {0} connection details (invalid host or one time session url already used?)", hostId); + return false; + } + + loginServer = !string.IsNullOrEmpty(connection.HostAddress) ? connection.HostAddress : connection.HostName; + loginDomain = string.Empty; // domain is defined into myrtille services config + loginUser = connection.Username; + loginPassword = RDPCryptoHelper.DecryptPassword(connection.Password); + loginProtocol = connection.Protocol; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve host {0} connection details ({1})", hostId, exc); + return false; + } + } + + // remote clipboard access + var allowRemoteClipboard = true; + if (bool.TryParse(ConfigurationManager.AppSettings["allowRemoteClipboard"], out bool bResult)) + { + allowRemoteClipboard = bResult; + } + + // remove any active remote session (disconnected?) + if (RemoteSession != null) + { + // unset the remote session for the current http session + HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] = null; + RemoteSession = null; } // create a new remote session @@ -200,22 +429,20 @@ protected void ConnectButtonClick( { Id = remoteSessionsCounter, State = RemoteSessionState.NotConnected, - ServerAddress = string.IsNullOrEmpty(server.Value) ? "localhost" : server.Value, - UserDomain = domain.Value, - UserName = user.Value, - UserPassword = string.IsNullOrEmpty(passwordHash.Value) ? password.Value : RDPCryptoHelper.DecryptPassword(passwordHash.Value), + ServerAddress = loginServer, + UserDomain = loginDomain, + UserName = loginUser, + UserPassword = loginPassword, ClientWidth = int.Parse(width.Value), ClientHeight = int.Parse(height.Value), - Program = program.Value + StartProgram = program.Value, + AllowRemoteClipboard = allowRemoteClipboard, + SecurityProtocol = loginProtocol }; - // set the remote session for the current http session + // bind the remote session to the current http session HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] = RemoteSession; - // register the http session at application level - var httpSessions = (IDictionary)HttpContext.Current.Application[HttpApplicationStateVariables.HttpSessions.ToString()]; - httpSessions[HttpContext.Current.Session.SessionID] = HttpContext.Current.Session; - // update the remote sessions auto-increment counter HttpContext.Current.Application[HttpApplicationStateVariables.RemoteSessionsCounter.ToString()] = remoteSessionsCounter; } @@ -245,21 +472,33 @@ protected void ConnectButtonClick( // use http://technet.microsoft.com/en-us/sysinternals/dd581625 to track the existing pipes RemoteSession.Manager.Client.StartProcess( RemoteSession.Id, + RemoteSession.ServerAddress, + RemoteSession.UserDomain, + RemoteSession.UserName, + RemoteSession.StartProgram, RemoteSession.ClientWidth, - RemoteSession.ClientHeight); - - // update controls - UpdateControls(); + RemoteSession.ClientHeight, + RemoteSession.AllowRemoteClipboard, + RemoteSession.SecurityProtocol); } catch (Exception exc) { System.Diagnostics.Trace.TraceError("Failed to connect the remote session {0} ({1})", RemoteSession.Id, exc); + connectError.InnerText = "Failed to connect! ensure myrtille services are running"; + return false; } } + else + { + connectError.InnerText = "Failed to create remote session!"; + return false; + } + + return true; } /// - /// stop the rdp session + /// disconnect the rdp server /// /// /// @@ -278,8 +517,13 @@ protected void DisconnectButtonClick( // send a disconnect command to the rdp client RemoteSession.Manager.SendCommand(RemoteSessionCommand.CloseRdpClient); - // update controls - UpdateControls(); + // if running in enterprise mode, redirect to the hosts list + // otherwise, redirect to the login screen + Response.Redirect("?", true); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect } catch (Exception exc) { @@ -287,5 +531,134 @@ protected void DisconnectButtonClick( } } } + + #region enterprise mode + + /// + /// create an enterprise session from a one time url + /// + private void CreateEnterpriseSessionFromUrl() + { + try + { + // create enterprise session from querystring params + _enterpriseSession = new EnterpriseSession + { + IsAdmin = false, // simple host connection only (no hosts management) + SessionID = Request["SI"], + SessionKey = Request["SK"] + }; + + // bind the enterprise session to the current http session + HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = _enterpriseSession; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to create enterprise session from url ({0})", exc); + } + } + + /// + /// authenticate the user against the enterprise active directory and list the servers available to the user + /// + private void CreateEnterpriseSessionFromLogin() + { + try + { + // authenticate the user against the enterprise active directory + _enterpriseSession = _enterpriseClient.Authenticate(user.Value, password.Value); + if (_enterpriseSession == null) + { + connectError.InnerText = "Active Directory Authentication failed!"; + UpdateControls(); + return; + } + + // bind the enterprise session to the current http session + HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = _enterpriseSession; + + // cancel the current http session + HttpContext.Current.Session.Abandon(); + + // prevent session fixation attack by generating a new session ID upon login + // also, using http get method to prevent the browser asking for http post data confirmation if the page is reloaded + // https://www.owasp.org/index.php/Session_Fixation + Response.Redirect(string.Format("?oldSID={0}", HttpContext.Current.Session.SessionID), true); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to create enterprise session from login ({0})", exc); + } + } + + /// + /// populate the enterprise session hosts list + /// + /// + /// + protected void hostsList_ItemDataBound( + object sender, + RepeaterItemEventArgs e) + { + try + { + var host = e.Item.DataItem as EnterpriseHost; + + var hostLink = e.Item.FindControl("hostLink") as HtmlAnchor; + hostLink.HRef = string.Format("?SD={0}&__EVENTTARGET=&__EVENTARGUMENT=&connect=Connect%21", host.HostID); + hostLink.Attributes["class"] = "hostLink"; + + var hostName = e.Item.FindControl("hostName") as HtmlGenericControl; + hostName.InnerText = host.HostName; + if (_enterpriseSession.IsAdmin) + { + hostName.Attributes["class"] = "hostName"; + hostName.Attributes["onclick"] = string.Format("openPopup('editHostPopup', 'EditHost.aspx?hostId={0}');", host.HostID); + } + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to populate hosts for the enterprise session {0} ({1})", _enterpriseSession.SessionID, exc); + } + } + + /// + /// logout the enterprise session + /// + /// + /// + protected void LogoutButtonClick( + object sender, + EventArgs e) + { + if (_enterpriseSession == null) + return; + + try + { + // logout the enterprise session + _enterpriseClient.Logout(_enterpriseSession.SessionID); + + // unbind the enterprise session from the current http session + HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] = null; + + // redirect to the login screen + Response.Redirect("?", true); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to logout the enterprise session {0} ({1})", _enterpriseSession.SessionID, exc); + } + } + + #endregion } } \ No newline at end of file diff --git a/Myrtille.Web/Default.aspx.designer.cs b/Myrtille.Web/Default.aspx.designer.cs index 69b37b2..ef45e8d 100644 --- a/Myrtille.Web/Default.aspx.designer.cs +++ b/Myrtille.Web/Default.aspx.designer.cs @@ -22,67 +22,67 @@ public partial class Default { protected global::System.Web.UI.HtmlControls.HtmlForm mainForm; /// - /// loginScreen control. + /// width control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl loginScreen; + protected global::System.Web.UI.HtmlControls.HtmlInputHidden width; /// - /// logo control. + /// height control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl logo; + protected global::System.Web.UI.HtmlControls.HtmlInputHidden height; /// - /// serverLabel control. + /// login control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl serverLabel; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl login; /// - /// server control. + /// logo control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputText server; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl logo; /// - /// domainLabel control. + /// domainServerDiv control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl domainLabel; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl domainServerDiv; /// - /// domain control. + /// server control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputText domain; + protected global::System.Web.UI.HtmlControls.HtmlInputText server; /// - /// userLabel control. + /// domain control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl userLabel; + protected global::System.Web.UI.HtmlControls.HtmlInputText domain; /// /// user control. @@ -94,40 +94,49 @@ public partial class Default { protected global::System.Web.UI.HtmlControls.HtmlInputText user; /// - /// passwordLabel control. + /// password control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl passwordLabel; + protected global::System.Web.UI.HtmlControls.HtmlInputPassword password; /// - /// password control. + /// passwordHash control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputPassword password; + protected global::System.Web.UI.HtmlControls.HtmlInputHidden passwordHash; /// - /// passwordHash control. + /// mfaDiv control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden passwordHash; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl mfaDiv; + + /// + /// mfaProvider control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlAnchor mfaProvider; /// - /// programLabel control. + /// mfaPassword control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlGenericControl programLabel; + protected global::System.Web.UI.HtmlControls.HtmlInputText mfaPassword; /// /// program control. @@ -139,31 +148,58 @@ public partial class Default { protected global::System.Web.UI.HtmlControls.HtmlInputText program; /// - /// width control. + /// connect control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden width; + protected global::System.Web.UI.HtmlControls.HtmlInputSubmit connect; /// - /// height control. + /// connectError control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden height; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl connectError; /// - /// connect control. + /// hosts control. /// /// /// Auto-generated field. /// To modify move field declaration from designer file to code-behind file. /// - protected global::System.Web.UI.HtmlControls.HtmlInputSubmit connect; + protected global::System.Web.UI.HtmlControls.HtmlGenericControl hosts; + + /// + /// newHost control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton newHost; + + /// + /// logout control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton logout; + + /// + /// hostsList control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Repeater hostsList; /// /// toolbar control. diff --git a/Myrtille.Web/Global.asax.cs b/Myrtille.Web/Global.asax.cs index 5768472..7022c7b 100644 --- a/Myrtille.Web/Global.asax.cs +++ b/Myrtille.Web/Global.asax.cs @@ -34,6 +34,7 @@ public enum HttpApplicationStateVariables public enum HttpSessionStateVariables { + EnterpriseSession, RemoteSession } diff --git a/Myrtille.Web/Myrtille.Web.csproj b/Myrtille.Web/Myrtille.Web.csproj index a7bd42c..db11135 100644 --- a/Myrtille.Web/Myrtille.Web.csproj +++ b/Myrtille.Web/Myrtille.Web.csproj @@ -25,6 +25,7 @@ + true @@ -83,39 +84,57 @@ - + + EditHostSession.aspx + ASPXCodeBehind + + + EditHostSession.aspx + + + EditHost.aspx + ASPXCodeBehind + + + EditHost.aspx + + FileStorage.aspx ASPXCodeBehind - + FileStorage.aspx + + ShowDialog.aspx + ASPXCodeBehind + + + ShowDialog.aspx + + + VirtualKeyboard.aspx + ASPXCodeBehind + + + VirtualKeyboard.aspx + SocketHandler.ashx + + + + Component - - ShowDialog.aspx - ASPXCodeBehind - - - ShowDialog.aspx - - - VirtualKeyboard.aspx - ASPXCodeBehind - - - VirtualKeyboard.aspx - Default.aspx ASPXCodeBehind @@ -126,8 +145,6 @@ Global.asax - - GetCursor.aspx @@ -161,7 +178,6 @@ - @@ -178,8 +194,11 @@ - - + + + + + @@ -216,7 +235,7 @@ Myrtille.Common - {010E1702-3045-4B13-BFB6-06FFC60B5CBB} + {010e1702-3045-4b13-bfb6-06ffc60b5cbb} Myrtille.Services.Contracts diff --git a/Myrtille.Web/Properties/AssemblyInfo.cs b/Myrtille.Web/Properties/AssemblyInfo.cs index 3506e57..9ab83fc 100644 --- a/Myrtille.Web/Properties/AssemblyInfo.cs +++ b/Myrtille.Web/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("1.7.0.0")] +[assembly: AssemblyVersion("1.8.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Myrtille.Web/PushUpdates.aspx.cs b/Myrtille.Web/PushUpdates.aspx.cs index 520e1fc..09afba3 100644 --- a/Myrtille.Web/PushUpdates.aspx.cs +++ b/Myrtille.Web/PushUpdates.aspx.cs @@ -85,7 +85,7 @@ protected void Page_Load( // disconnected session else if (remoteSession.State == RemoteSessionState.Disconnected) { - HttpContext.Current.Response.Write(""); + HttpContext.Current.Response.Write(""); HttpContext.Current.Response.Flush(); break; } diff --git a/Myrtille.Web/Web.Base.config b/Myrtille.Web/Web.Base.config index 494bf0c..d49605d 100644 --- a/Myrtille.Web/Web.Base.config +++ b/Myrtille.Web/Web.Base.config @@ -81,6 +81,8 @@ + + @@ -104,6 +106,10 @@ + + + + \ No newline at end of file diff --git a/Myrtille.Web/css/Default.css b/Myrtille.Web/css/Default.css index 7ba7285..94d14f8 100644 --- a/Myrtille.Web/css/Default.css +++ b/Myrtille.Web/css/Default.css @@ -46,7 +46,7 @@ a img /*** LOGIN ***/ /*****************************************************************************************************************************************************************************************************/ -#loginScreen +#login { position: absolute; top: 50%; @@ -72,17 +72,29 @@ a img display: inline-block; } -label +label, +#mfaProvider { float: left; width: 200px; margin-top: 2px; } +#mfaProvider +{ + cursor: pointer; +} + +#mfaProvider:hover +{ + text-decoration: underline; +} + #server, #domain, #user, #password, +#mfaPassword, #program { width: 200px; @@ -111,6 +123,54 @@ label display: inline-block; } +#errorDiv +{ + margin-left: 200px; +} + +#connectError +{ + color: #ff0000; +} + +/*****************************************************************************************************************************************************************************************************/ +/*** HOSTS ***/ +/*****************************************************************************************************************************************************************************************************/ + +#hosts +{ + /* if needed */ +} + +#hostsControl +{ + /* if needed */ +} + +.hostDiv +{ + display: inline-block; + text-align: center; + font-size: 15px; +} + +.hostDiv:hover +{ + background-color: #ecf0f1; + color: #000; +} + +.hostLink, +.hostName +{ + cursor: pointer; +} + +.hostName:hover +{ + text-decoration: underline; +} + /*****************************************************************************************************************************************************************************************************/ /*** TOOLBAR ***/ /*****************************************************************************************************************************************************************************************************/ @@ -185,42 +245,119 @@ label z-index: 400; } +/* virtual keyboard popup */ + #virtualKeyboardPopup { width: 470px; height: 290px; } +#virtualKeyboardPopupDesc +{ + font-weight: bold; +} + +#virtualKeyboardPopupText +{ + width: 425px; + height: 150px; + margin-bottom: 5px; +} + +/* show dialog popup */ + #showDialogPopup { width: 470px; height: 240px; } +#showDialogPopupDesc +{ + font-weight: bold; +} + +#showDialogPopupText +{ + width: 425px; + height: 150px; + margin-bottom: 5px; +} + +/* file storage popup */ + #fileStoragePopup { width: 470px; height: 130px; } -#virtualKeyboardPopupDesc, -#showDialogPopupDesc, #fileStoragePopupDesc { font-weight: bold; } -#virtualKeyboardPopupText, -#showDialogPopupText +#fileToDownloadSelect { - width: 425px; - height: 150px; - margin-bottom: 5px; + width: 250px; } -#fileToDownloadSelect +/* host popup */ + +#editHostPopup, +#editHostPopupInner { - width: 250px; + width: 400px; + height: 270px; +} + +#editHostPopupInner +{ + color: #000; + background: #fff; +} + +#editHostPopupTitle +{ + padding-top: 20px; + padding-left: 10px; +} + +.editHostPopupInput +{ + padding-left: 10px; +} + +/* host session popup */ + +#editHostSessionPopup, +#editHostSessionPopupInner +{ + width: 400px; + height: 400px; +} + +#editHostSessionPopupInner +{ + color: #000; + background: #fff; +} + +#editHostSessionPopupTitle +{ + padding-top: 20px; + padding-left: 10px; +} + +.editHostSessionPopupInput +{ + padding-left: 10px; +} + +#sessionUrl +{ + width: 370px; } /* remote session display */ diff --git a/Myrtille.Web/js/dialog.js b/Myrtille.Web/js/dialog.js index daf8d2e..971ea32 100644 --- a/Myrtille.Web/js/dialog.js +++ b/Myrtille.Web/js/dialog.js @@ -404,37 +404,58 @@ var popup = null; function openPopup(id, src) { - // lock background - var bgfDiv = document.getElementById('bgfDiv'); - if (bgfDiv != null) + try { - bgfDiv.style.visibility = 'visible'; - bgfDiv.style.display = 'block'; - } + // if there is already an opened popup, close it + if (popup != null) + { + closePopup(); + } + + // lock background + var bgfDiv = document.getElementById('bgfDiv'); + if (bgfDiv != null) + { + bgfDiv.style.visibility = 'visible'; + bgfDiv.style.display = 'block'; + } - // add popup - popup = document.createElement('iframe'); - popup.id = id; - popup.src = src; - popup.className = 'modalPopup'; + // add popup + popup = document.createElement('iframe'); + popup.id = id; + popup.src = 'popups/' + src; + popup.className = 'modalPopup'; - document.body.appendChild(popup); + document.body.appendChild(popup); + } + catch (exc) + { + this.showDebug('openPopup error: ' + exc.message); + } } function closePopup() { - // remove popup - if (popup != null) + try { - document.body.removeChild(popup); - } + // remove popup + if (popup != null) + { + document.body.removeChild(popup); + popup = null; + } - // unlock background - var bgfDiv = document.getElementById('bgfDiv'); - if (bgfDiv != null) + // unlock background + var bgfDiv = document.getElementById('bgfDiv'); + if (bgfDiv != null) + { + bgfDiv.style.visibility = 'hidden'; + bgfDiv.style.display = 'none'; + } + } + catch (exc) { - bgfDiv.style.visibility = 'hidden'; - bgfDiv.style.display = 'none'; + this.showDebug('closePopup error: ' + exc.message); } } diff --git a/Myrtille.Web/js/myrtille.js b/Myrtille.Web/js/myrtille.js index 7ae0705..145e1ae 100644 --- a/Myrtille.Web/js/myrtille.js +++ b/Myrtille.Web/js/myrtille.js @@ -107,58 +107,6 @@ function startMyrtille(remoteSessionActive, statEnabled, debugEnabled, compatibi var httpServerUrl = window.location.protocol + '//' + window.location.hostname + (window.location.port ? ':' + window.location.port : '') + '/' + pathname + '/'; //alert('http server url: ' + httpServerUrl); - // prevent session fixation attack by generating a new session ID upon login - // also, using http get method to prevent the browser asking for http post data confirmation if the page is reloaded - // https://www.owasp.org/index.php/Session_Fixation - var redirectUrl = ''; - - if (window.location.href.indexOf('?') == -1) - { - //alert('no querystring detected'); - // retrieve the http session id from url; in cookieless mode (default), it has the format "(S(sessionId))" - // reload the page without it, so that a new session is generated while the current one is abandonned - var startIdx = httpServerUrl.indexOf('(S('); - if (startIdx != -1) - { - var endIdx = httpServerUrl.indexOf('))'); - var httpSessionId = httpServerUrl.substr(startIdx + 3, endIdx - startIdx - 3); - //alert('http session id: ' + httpSessionId); - redirectUrl = httpServerUrl.replace('/(S(' + httpSessionId + '))', '') + '?oldSID=' + httpSessionId; - } - // http session id missing from url - // the http session may use a cookie (CAUTION! in this case, multi tabs/sessions support is disabled) - // simple redirect with an empty querystring - else - { - //alert('no http session id into url'); - redirectUrl = httpServerUrl + '?'; - } - } - else - { - //alert('querystring detected'); - // the http session used on login was abandonned; remove it from url - if (window.location.href.indexOf('?oldSID=') != -1) - { - //alert('clearing old session id from url'); - redirectUrl = window.location.href.substr(0, window.location.href.indexOf('?oldSID=')) + '?'; - } - // auto-connect / start program from url - // there is no need for any session fixation protection in this case because the user credentials are already into the url (with password either plain text or hashed) - else if (window.location.href.indexOf('&connect=') != -1) - { - // redirect with an empty querystring - redirectUrl = httpServerUrl + '?'; - } - } - - if (redirectUrl != '') - { - //alert('reloading page with url: ' + redirectUrl); - window.location.href = redirectUrl; - return; - } - myrtille = new Myrtille(httpServerUrl, statEnabled, debugEnabled, compatibilityMode, scaleDisplay, displayWidth, displayHeight); myrtille.init(); diff --git a/Myrtille.Web/js/network/websocket.js b/Myrtille.Web/js/network/websocket.js index a31ec35..16bac1d 100644 --- a/Myrtille.Web/js/network/websocket.js +++ b/Myrtille.Web/js/network/websocket.js @@ -225,7 +225,7 @@ function Websocket(config, dialog, display, network) // disconnected session else if (message == 'disconnected') { - window.location.href = config.getHttpServerUrl(); + window.location.href = config.getHttpServerUrl() + '?'; } // server ack else if (message.length >= 4 && message.substr(0, 4) == 'ack,') diff --git a/Myrtille.Web/js/network/xmlhttp.js b/Myrtille.Web/js/network/xmlhttp.js index 7e7a911..347215c 100644 --- a/Myrtille.Web/js/network/xmlhttp.js +++ b/Myrtille.Web/js/network/xmlhttp.js @@ -224,7 +224,7 @@ function XmlHttp(config, dialog, display, network) // disconnected session else if (xhrResponseText == 'disconnected') { - window.location.href = config.getHttpServerUrl(); + window.location.href = config.getHttpServerUrl() + '?'; } // new image else diff --git a/Myrtille.Web/popups/EditHost.aspx b/Myrtille.Web/popups/EditHost.aspx new file mode 100644 index 0000000..310e3ed --- /dev/null +++ b/Myrtille.Web/popups/EditHost.aspx @@ -0,0 +1,91 @@ +<%-- + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> + +<%@ Page Language="C#" Inherits="Myrtille.Web.EditHost" Codebehind="EditHost.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> +<%@ OutputCache Location="None" %> + + + + + + + Myrtille + + + + + + + +
+ + Host Configuration + +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ + + + +
+
+ + + + + + + + \ No newline at end of file diff --git a/Myrtille.Web/popups/EditHost.aspx.cs b/Myrtille.Web/popups/EditHost.aspx.cs new file mode 100644 index 0000000..e09462e --- /dev/null +++ b/Myrtille.Web/popups/EditHost.aspx.cs @@ -0,0 +1,178 @@ +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Threading; +using System.Web; +using System.Web.UI; +using Myrtille.Services.Contracts; + +namespace Myrtille.Web +{ + public partial class EditHost : Page + { + private EnterpriseServiceClient _enterpriseClient; + private EnterpriseSession _enterpriseSession; + private long? _hostId = null; + + /// + /// page init + /// + /// + /// + protected void Page_Init( + object sender, + EventArgs e) + { + _enterpriseClient = new EnterpriseServiceClient(); + } + + /// + /// page load (postback data is now available) + /// + /// + /// + protected void Page_Load( + object sender, + EventArgs e) + { + // retrieve the active enterprise session, if any + if (HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) + { + try + { + _enterpriseSession = (EnterpriseSession)HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve the enterprise session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); + } + } + + // retrieve the host + if (Request["hostId"] != null) + { + if (long.TryParse(Request["hostId"], out long lResult)) + { + _hostId = lResult; + } + + if (!IsPostBack && Request["edit"] == null) + { + try + { + var host = _enterpriseClient.GetHost(_hostId.Value, _enterpriseSession.SessionID); + if (host != null) + { + hostName.Value = host.HostName; + hostAddress.Value = host.HostAddress; + groupsAccess.Value = host.DirectoryGroups; + securityProtocol.SelectedIndex = (int)host.Protocol; + } + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve host {0}, ({1})", _hostId, exc); + } + } + + createSessionUrl.Attributes["onclick"] = string.Format("parent.openPopup('editHostSessionPopup', 'EditHostSession.aspx?hostId={0}');", _hostId); + } + else + { + createSessionUrl.Disabled = true; + deleteHost.Disabled = true; + } + } + + /// + /// create or edit a host + /// + /// + /// + protected void SaveHostButtonClick( + object sender, + EventArgs e) + { + if (_enterpriseClient == null || _enterpriseSession == null || string.IsNullOrEmpty(hostName.Value)) + return; + + try + { + if (!_hostId.HasValue) + { + _enterpriseClient.AddHost(new EnterpriseHostEdit + { + HostID = 0, + HostName = hostName.Value, + HostAddress = hostAddress.Value, + DirectoryGroups = groupsAccess.Value, + Protocol = (SecurityProtocolEnum)securityProtocol.SelectedIndex + }, _enterpriseSession.SessionID); + } + else + { + _enterpriseClient.UpdateHost(new EnterpriseHostEdit + { + HostID = _hostId.Value, + HostName = hostName.Value, + HostAddress = hostAddress.Value, + DirectoryGroups = groupsAccess.Value, + Protocol = (SecurityProtocolEnum)securityProtocol.SelectedIndex + }, _enterpriseSession.SessionID); + } + + // refresh the hosts list + Response.Redirect(Request.RawUrl + (Request.RawUrl.Contains("?") ? "&" : "?") + "edit=success"); + } + catch (ThreadAbortException) + { + // occurs because the response is ended after redirect + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to save host ({0})", exc); + } + } + + /// + /// delete a host + /// + /// + /// + protected void DeleteHostButtonClick( + object sender, + EventArgs e) + { + if (_enterpriseClient == null || _enterpriseSession == null || !_hostId.HasValue) + return; + + try + { + _enterpriseClient.DeleteHost(_hostId.Value, _enterpriseSession.SessionID); + + // refresh the hosts list + Response.Redirect(Request.RawUrl + (Request.RawUrl.Contains("?") ? "&" : "?") + "edit=success"); + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to delete host {0} ({1})", _hostId.Value, exc); + } + } + } +} \ No newline at end of file diff --git a/Myrtille.Web/popups/EditHost.aspx.designer.cs b/Myrtille.Web/popups/EditHost.aspx.designer.cs new file mode 100644 index 0000000..9befaed --- /dev/null +++ b/Myrtille.Web/popups/EditHost.aspx.designer.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Myrtille.Web { + + + public partial class EditHost { + + /// + /// hostName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputText hostName; + + /// + /// hostAddress control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputText hostAddress; + + /// + /// groupsAccess control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputText groupsAccess; + + /// + /// securityProtocol control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlSelect securityProtocol; + + /// + /// createSessionUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton createSessionUrl; + + /// + /// saveHost control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton saveHost; + + /// + /// deleteHost control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton deleteHost; + } +} diff --git a/Myrtille.Web/popups/EditHostSession.aspx b/Myrtille.Web/popups/EditHostSession.aspx new file mode 100644 index 0000000..8c06bcc --- /dev/null +++ b/Myrtille.Web/popups/EditHostSession.aspx @@ -0,0 +1,65 @@ +<%-- + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> + +<%@ Page Language="C#" Inherits="Myrtille.Web.EditHostSession" Codebehind="EditHostSession.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> +<%@ OutputCache Location="None" %> + + + + + + + Myrtille + + + + + +
+ +
+ + Create Host Session for + +
+
+
+ +
+
+
+ +
+
+
Session URL
+ +
+
+ +
Copy the URL and use how required; it can only be used once
+
+
+ +
+
+ +
+ + + + \ No newline at end of file diff --git a/Myrtille.Web/popups/EditHostSession.aspx.cs b/Myrtille.Web/popups/EditHostSession.aspx.cs new file mode 100644 index 0000000..32fdd19 --- /dev/null +++ b/Myrtille.Web/popups/EditHostSession.aspx.cs @@ -0,0 +1,115 @@ +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.Web; +using System.Web.UI; +using Myrtille.Services.Contracts; + +namespace Myrtille.Web +{ + public partial class EditHostSession : Page + { + private EnterpriseServiceClient _enterpriseClient; + private EnterpriseSession _enterpriseSession; + private long? _hostId = null; + + /// + /// page init + /// + /// + /// + protected void Page_Init( + object sender, + EventArgs e) + { + _enterpriseClient = new EnterpriseServiceClient(); + } + + /// + /// page load (postback data is now available) + /// + /// + /// + protected void Page_Load( + object sender, + EventArgs e) + { + // retrieve the active enterprise session, if any + if (HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()] != null) + { + try + { + _enterpriseSession = (EnterpriseSession)HttpContext.Current.Session[HttpSessionStateVariables.EnterpriseSession.ToString()]; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve the enterprise session for the http session {0}, ({1})", HttpContext.Current.Session.SessionID, exc); + } + } + + // retrieve the host + if (Request["hostId"] != null) + { + if (long.TryParse(Request["hostId"], out long lResult)) + { + _hostId = lResult; + } + + try + { + var host = _enterpriseClient.GetHost(_hostId.Value, _enterpriseSession.SessionID); + if (host != null) + { + hostName.InnerText = host.HostName; + } + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve host {0}, ({1})", _hostId, exc); + } + } + } + + /// + /// create an host session URL + /// + /// + /// + protected void CreateSessionUrlButtonClick( + object sender, + EventArgs e) + { + if (_enterpriseClient == null || _enterpriseSession == null || !_hostId.HasValue || string.IsNullOrEmpty(userName.Value) || string.IsNullOrEmpty(userPassword.Value)) + return; + + try + { + var url = _enterpriseClient.CreateUserSession(_enterpriseSession.SessionID, _hostId.Value, userName.Value, userPassword.Value); + if (!string.IsNullOrEmpty(url)) + { + sessionUrl.Value = Request.Url.Scheme + "://" + Request.Url.Host + (Request.Url.Port != 80 && Request.Url.Port != 443 ? ":" + Request.Url.Port : "") + "/" + Request.Url.Segments[1] + url + "&__EVENTTARGET=&__EVENTARGUMENT=&connect=Connect%21"; + } + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to create session url for host {0} ({1})", _hostId, exc); + } + } + } +} \ No newline at end of file diff --git a/Myrtille.Web/popups/EditHostSession.aspx.designer.cs b/Myrtille.Web/popups/EditHostSession.aspx.designer.cs new file mode 100644 index 0000000..08696cb --- /dev/null +++ b/Myrtille.Web/popups/EditHostSession.aspx.designer.cs @@ -0,0 +1,60 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Myrtille.Web { + + + public partial class EditHostSession { + + /// + /// hostName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlGenericControl hostName; + + /// + /// userName control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputText userName; + + /// + /// userPassword control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputPassword userPassword; + + /// + /// createSessionUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton createSessionUrl; + + /// + /// sessionUrl control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlTextArea sessionUrl; + } +} diff --git a/Myrtille.Web/FileStorage.aspx b/Myrtille.Web/popups/FileStorage.aspx similarity index 95% rename from Myrtille.Web/FileStorage.aspx rename to Myrtille.Web/popups/FileStorage.aspx index d5455e6..4331358 100644 --- a/Myrtille.Web/FileStorage.aspx +++ b/Myrtille.Web/popups/FileStorage.aspx @@ -1,83 +1,83 @@ -<%-- - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---%> - -<%@ Page Language="C#" Inherits="Myrtille.Web.FileStorage" Codebehind="FileStorage.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> -<%@ OutputCache Location="None" %> - - - - - - - Myrtille - - - - - -
- - -
- Files into "My documents" folder
- Upload file: -
- Download file:
- -
- -
- - - - - +<%-- + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> + +<%@ Page Language="C#" Inherits="Myrtille.Web.FileStorage" Codebehind="FileStorage.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> +<%@ OutputCache Location="None" %> + + + + + + + Myrtille + + + + + +
+ + +
+ Files into "My documents" folder
+ Upload file: +
+ Download file:
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/Myrtille.Web/FileStorage.aspx.cs b/Myrtille.Web/popups/FileStorage.aspx.cs similarity index 95% rename from Myrtille.Web/FileStorage.aspx.cs rename to Myrtille.Web/popups/FileStorage.aspx.cs index f885b9d..7802a65 100644 --- a/Myrtille.Web/FileStorage.aspx.cs +++ b/Myrtille.Web/popups/FileStorage.aspx.cs @@ -1,177 +1,177 @@ -/* - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System; -using System.IO; -using System.Threading; -using System.Web; -using System.Web.UI; -using Myrtille.Helpers; -using Myrtille.Services.Contracts; - -namespace Myrtille.Web -{ - public partial class FileStorage : Page - { - private RemoteSession _remoteSession; - private FileStorageClient _fileStorageClient; - - /// - /// initialization - /// - /// - /// - protected void Page_Init( - object sender, - EventArgs e) - { - try - { - // retrieve the active remote session, if any - if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) - { - try - { - if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] == null) - throw new NullReferenceException(); - - _remoteSession = (RemoteSession)HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()]; - } - catch (Exception exc) - { - System.Diagnostics.Trace.TraceError("Failed to retrieve the remote session ({0})", exc); - } - } - - // ensure there is an active remote session - // if a domain is specified, the roaming user profile is loaded from the Active Directory - // file storage is synchronized with the user "My documents" folder (will use folder redirection if defined) - // user credentials will be checked prior to any file operation - // if possible, use SSL to communicate with the service - if (_remoteSession != null && (_remoteSession.State == RemoteSessionState.Connecting || _remoteSession.State == RemoteSessionState.Connected) && - (_remoteSession.ServerAddress.ToLower() == "localhost" || _remoteSession.ServerAddress == "127.0.0.1" || _remoteSession.ServerAddress == HttpContext.Current.Request.Url.Host || !string.IsNullOrEmpty(_remoteSession.UserDomain)) && - !string.IsNullOrEmpty(_remoteSession.UserName) && !string.IsNullOrEmpty(_remoteSession.UserPassword)) - { - _fileStorageClient = new FileStorageClient(); - - var files = _fileStorageClient.GetUserDocumentsFolderFiles( - _remoteSession.Id, - _remoteSession.UserDomain, - _remoteSession.UserName, - _remoteSession.UserPassword); - - if (files.Count > 0) - { - fileToDownloadSelect.DataSource = files; - fileToDownloadSelect.DataBind(); - downloadFileButton.Disabled = false; - } - } - } - catch (Exception exc) - { - System.Diagnostics.Trace.TraceError("Failed to init file storage ({0})", exc); - } - } - - /// - /// upload a file to the user "My documents" folder - /// - /// - /// - protected void UploadFileButtonClick( - object sender, - EventArgs e) - { - try - { - if (_fileStorageClient == null) - return; - - if (fileToUploadText.PostedFile == null) - { - System.Diagnostics.Trace.TraceInformation("File to upload is missing"); - } - else if (fileToUploadText.PostedFile.ContentLength == 0) - { - System.Diagnostics.Trace.TraceInformation("File to upload is empty"); - } - else - { - _fileStorageClient.UploadFileToUserDocumentsFolder( - new UploadRequest - { - RemoteSessionId = _remoteSession.Id, - UserDomain = _remoteSession.UserDomain, - UserName = _remoteSession.UserName, - UserPassword = _remoteSession.UserPassword, - FileName = Path.GetFileName(fileToUploadText.PostedFile.FileName), - FileStream = fileToUploadText.PostedFile.InputStream - }); - - // reload the page to have the newly uploaded file available for download - Response.Redirect(Request.RawUrl + (Request.RawUrl.Contains("?") ? "&" : "?") + "upload=success"); - } - } - catch (ThreadAbortException) - { - // occurs because the response is ended after reloading the page - } - catch (Exception exc) - { - System.Diagnostics.Trace.TraceError("Failed to upload file ({0})", exc); - } - } - - /// - /// download a file from the user "My documents" folder - /// - /// - /// - protected void DownloadFileButtonClick( - object sender, - EventArgs e) - { - try - { - if (_fileStorageClient == null) - return; - - if (!string.IsNullOrEmpty(fileToDownloadSelect.Value)) - { - var fileStream = _fileStorageClient.DownloadFileFromUserDocumentsFolder( - _remoteSession.Id, - _remoteSession.UserDomain, - _remoteSession.UserName, - _remoteSession.UserPassword, - fileToDownloadSelect.Value); - - FileHelper.DownloadFile(Response, fileStream, fileToDownloadSelect.Value, true); - } - } - catch (ThreadAbortException) - { - // occurs because the response is ended after sending the file content - } - catch (Exception exc) - { - System.Diagnostics.Trace.TraceError("Failed to download file ({0})", exc); - } - } - } +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System; +using System.IO; +using System.Threading; +using System.Web; +using System.Web.UI; +using Myrtille.Helpers; +using Myrtille.Services.Contracts; + +namespace Myrtille.Web +{ + public partial class FileStorage : Page + { + private RemoteSession _remoteSession; + private FileStorageClient _fileStorageClient; + + /// + /// page init + /// + /// + /// + protected void Page_Init( + object sender, + EventArgs e) + { + try + { + // retrieve the active remote session, if any + if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] != null) + { + try + { + if (HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()] == null) + throw new NullReferenceException(); + + _remoteSession = (RemoteSession)HttpContext.Current.Session[HttpSessionStateVariables.RemoteSession.ToString()]; + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to retrieve the remote session ({0})", exc); + } + } + + // ensure there is an active remote session + // if a domain is specified, the roaming user profile is loaded from the Active Directory + // file storage is synchronized with the user "My documents" folder (will use folder redirection if defined) + // user credentials will be checked prior to any file operation + // if possible, use SSL to communicate with the service + if (_remoteSession != null && (_remoteSession.State == RemoteSessionState.Connecting || _remoteSession.State == RemoteSessionState.Connected) && + (_remoteSession.ServerAddress.ToLower() == "localhost" || _remoteSession.ServerAddress == "127.0.0.1" || _remoteSession.ServerAddress == "[::1]" || _remoteSession.ServerAddress == HttpContext.Current.Request.Url.Host || !string.IsNullOrEmpty(_remoteSession.UserDomain)) && + !string.IsNullOrEmpty(_remoteSession.UserName) && !string.IsNullOrEmpty(_remoteSession.UserPassword)) + { + _fileStorageClient = new FileStorageClient(); + + var files = _fileStorageClient.GetUserDocumentsFolderFiles( + _remoteSession.Id, + _remoteSession.UserDomain, + _remoteSession.UserName, + _remoteSession.UserPassword); + + if (files.Count > 0) + { + fileToDownloadSelect.DataSource = files; + fileToDownloadSelect.DataBind(); + downloadFileButton.Disabled = false; + } + } + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to init file storage ({0})", exc); + } + } + + /// + /// upload a file to the user "My documents" folder + /// + /// + /// + protected void UploadFileButtonClick( + object sender, + EventArgs e) + { + try + { + if (_fileStorageClient == null) + return; + + if (fileToUploadText.PostedFile == null) + { + System.Diagnostics.Trace.TraceInformation("File to upload is missing"); + } + else if (fileToUploadText.PostedFile.ContentLength == 0) + { + System.Diagnostics.Trace.TraceInformation("File to upload is empty"); + } + else + { + _fileStorageClient.UploadFileToUserDocumentsFolder( + new UploadRequest + { + RemoteSessionId = _remoteSession.Id, + UserDomain = _remoteSession.UserDomain, + UserName = _remoteSession.UserName, + UserPassword = _remoteSession.UserPassword, + FileName = Path.GetFileName(fileToUploadText.PostedFile.FileName), + FileStream = fileToUploadText.PostedFile.InputStream + }); + + // reload the page to have the newly uploaded file available for download + Response.Redirect(Request.RawUrl + (Request.RawUrl.Contains("?") ? "&" : "?") + "upload=success"); + } + } + catch (ThreadAbortException) + { + // occurs because the response is ended after reloading the page + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to upload file ({0})", exc); + } + } + + /// + /// download a file from the user "My documents" folder + /// + /// + /// + protected void DownloadFileButtonClick( + object sender, + EventArgs e) + { + try + { + if (_fileStorageClient == null) + return; + + if (!string.IsNullOrEmpty(fileToDownloadSelect.Value)) + { + var fileStream = _fileStorageClient.DownloadFileFromUserDocumentsFolder( + _remoteSession.Id, + _remoteSession.UserDomain, + _remoteSession.UserName, + _remoteSession.UserPassword, + fileToDownloadSelect.Value); + + FileHelper.DownloadFile(Response, fileStream, fileToDownloadSelect.Value, true); + } + } + catch (ThreadAbortException) + { + // occurs because the response is ended after sending the file content + } + catch (Exception exc) + { + System.Diagnostics.Trace.TraceError("Failed to download file ({0})", exc); + } + } + } } \ No newline at end of file diff --git a/Myrtille.Web/FileStorage.aspx.designer.cs b/Myrtille.Web/popups/FileStorage.aspx.designer.cs similarity index 97% rename from Myrtille.Web/FileStorage.aspx.designer.cs rename to Myrtille.Web/popups/FileStorage.aspx.designer.cs index e81ecd9..c0c5614 100644 --- a/Myrtille.Web/FileStorage.aspx.designer.cs +++ b/Myrtille.Web/popups/FileStorage.aspx.designer.cs @@ -1,51 +1,51 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Myrtille.Web { - - - public partial class FileStorage { - - /// - /// fileToUploadText control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputFile fileToUploadText; - - /// - /// uploadFileButton control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputButton uploadFileButton; - - /// - /// fileToDownloadSelect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlSelect fileToDownloadSelect; - - /// - /// downloadFileButton control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputButton downloadFileButton; - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Myrtille.Web { + + + public partial class FileStorage { + + /// + /// fileToUploadText control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputFile fileToUploadText; + + /// + /// uploadFileButton control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton uploadFileButton; + + /// + /// fileToDownloadSelect control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlSelect fileToDownloadSelect; + + /// + /// downloadFileButton control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.HtmlControls.HtmlInputButton downloadFileButton; + } +} diff --git a/Myrtille.Web/ShowDialog.aspx b/Myrtille.Web/popups/ShowDialog.aspx similarity index 90% rename from Myrtille.Web/ShowDialog.aspx rename to Myrtille.Web/popups/ShowDialog.aspx index 15b8d95..1fdb09d 100644 --- a/Myrtille.Web/ShowDialog.aspx +++ b/Myrtille.Web/popups/ShowDialog.aspx @@ -1,70 +1,70 @@ -<%-- - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---%> - -<%@ Page Language="C#" Inherits="Myrtille.Web.ShowDialog" Codebehind="ShowDialog.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> -<%@ OutputCache Location="None" %> - - - - - - - Myrtille - - - - - -
- - -
-
-
- -
- -
- - - - - +<%-- + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> + +<%@ Page Language="C#" Inherits="Myrtille.Web.ShowDialog" Codebehind="ShowDialog.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> +<%@ OutputCache Location="None" %> + + + + + + + Myrtille + + + + + +
+ + +
+
+
+ +
+ +
+ + + + + \ No newline at end of file diff --git a/Myrtille.Web/ShowDialog.aspx.cs b/Myrtille.Web/popups/ShowDialog.aspx.cs similarity index 96% rename from Myrtille.Web/ShowDialog.aspx.cs rename to Myrtille.Web/popups/ShowDialog.aspx.cs index ac80041..2feed76 100644 --- a/Myrtille.Web/ShowDialog.aspx.cs +++ b/Myrtille.Web/popups/ShowDialog.aspx.cs @@ -1,26 +1,26 @@ -/* - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Web.UI; - -namespace Myrtille.Web -{ - public partial class ShowDialog : Page - { - } +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Web.UI; + +namespace Myrtille.Web +{ + public partial class ShowDialog : Page + { + } } \ No newline at end of file diff --git a/Myrtille.Web/ShowDialog.aspx.designer.cs b/Myrtille.Web/popups/ShowDialog.aspx.designer.cs similarity index 96% rename from Myrtille.Web/ShowDialog.aspx.designer.cs rename to Myrtille.Web/popups/ShowDialog.aspx.designer.cs index 97b446c..89398d7 100644 --- a/Myrtille.Web/ShowDialog.aspx.designer.cs +++ b/Myrtille.Web/popups/ShowDialog.aspx.designer.cs @@ -1,15 +1,15 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Myrtille.Web { - - - public partial class ShowDialog { - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Myrtille.Web { + + + public partial class ShowDialog { + } +} diff --git a/Myrtille.Web/VirtualKeyboard.aspx b/Myrtille.Web/popups/VirtualKeyboard.aspx similarity index 95% rename from Myrtille.Web/VirtualKeyboard.aspx rename to Myrtille.Web/popups/VirtualKeyboard.aspx index 928c98d..e8e914a 100644 --- a/Myrtille.Web/VirtualKeyboard.aspx +++ b/Myrtille.Web/popups/VirtualKeyboard.aspx @@ -1,65 +1,65 @@ -<%-- - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---%> - -<%@ Page Language="C#" Inherits="Myrtille.Web.VirtualKeyboard" Codebehind="VirtualKeyboard.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> -<%@ OutputCache Location="None" %> - - - - - - - Myrtille - - - - - -
- - - - -
- - Type or paste some text then click send
- Alternatively, you can use the Windows on screen keyboard (%SystemRoot%\System32\osk.exe) within the session -

-
- - -
- -
- - - - - +<%-- + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--%> + +<%@ Page Language="C#" Inherits="Myrtille.Web.VirtualKeyboard" Codebehind="VirtualKeyboard.aspx.cs" AutoEventWireup="true" Culture="auto" UICulture="auto" %> +<%@ OutputCache Location="None" %> + + + + + + + Myrtille + + + + + +
+ + + + +
+ + Type or paste some text then click send
+ Alternatively, you can use the Windows on screen keyboard (%SystemRoot%\System32\osk.exe) within the session +

+
+ + +
+ +
+ + + + + \ No newline at end of file diff --git a/Myrtille.Web/VirtualKeyboard.aspx.cs b/Myrtille.Web/popups/VirtualKeyboard.aspx.cs similarity index 96% rename from Myrtille.Web/VirtualKeyboard.aspx.cs rename to Myrtille.Web/popups/VirtualKeyboard.aspx.cs index 9996ece..1ab1dc4 100644 --- a/Myrtille.Web/VirtualKeyboard.aspx.cs +++ b/Myrtille.Web/popups/VirtualKeyboard.aspx.cs @@ -1,26 +1,26 @@ -/* - Myrtille: A native HTML4/5 Remote Desktop Protocol client. - - Copyright(c) 2014-2018 Cedric Coste - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -using System.Web.UI; - -namespace Myrtille.Web -{ - public partial class VirtualKeyboard : Page - { - } +/* + Myrtille: A native HTML4/5 Remote Desktop Protocol client. + + Copyright(c) 2014-2018 Cedric Coste + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +using System.Web.UI; + +namespace Myrtille.Web +{ + public partial class VirtualKeyboard : Page + { + } } \ No newline at end of file diff --git a/Myrtille.Web/VirtualKeyboard.aspx.designer.cs b/Myrtille.Web/popups/VirtualKeyboard.aspx.designer.cs similarity index 96% rename from Myrtille.Web/VirtualKeyboard.aspx.designer.cs rename to Myrtille.Web/popups/VirtualKeyboard.aspx.designer.cs index fc14095..38baaf6 100644 --- a/Myrtille.Web/VirtualKeyboard.aspx.designer.cs +++ b/Myrtille.Web/popups/VirtualKeyboard.aspx.designer.cs @@ -1,15 +1,15 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Myrtille.Web { - - - public partial class VirtualKeyboard { - } -} +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Myrtille.Web { + + + public partial class VirtualKeyboard { + } +} diff --git a/Myrtille.Web/src/EnterpriseServiceClient.cs b/Myrtille.Web/src/EnterpriseServiceClient.cs new file mode 100644 index 0000000..01f1a0c --- /dev/null +++ b/Myrtille.Web/src/EnterpriseServiceClient.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.ServiceModel; +using Myrtille.Services.Contracts; + +namespace Myrtille.Web +{ + public class EnterpriseServiceClient : ClientBase, IEnterpriseService + { + public bool GetState() + { + try + { + return Channel.GetState(); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get enterprise adapter state ({0})", exc); + return false; + } + } + + public EnterpriseSession Authenticate(string username, string password) + { + try + { + return Channel.Authenticate(username, password); + } + catch (Exception exc) + { + Trace.TraceError("Failed to authenticate enterprise user {0} ({1})", username, exc); + return null; + } + } + + public void Logout(string sessionID) + { + try + { + Channel.Logout(sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to logout enterprise session ({0})", exc); + throw; + } + } + + public long? AddHost(EnterpriseHostEdit editHost, string sessionID) + { + try + { + return Channel.AddHost(editHost, sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to add host {0} ({1})", editHost.HostName, exc); + throw; + } + } + + public EnterpriseHostEdit GetHost(long hostID, string sessionID) + { + try + { + return Channel.GetHost(hostID, sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get host {0} ({1})", hostID, exc); + throw; + } + } + + public bool UpdateHost(EnterpriseHostEdit editHost, string sessionID) + { + try + { + return Channel.UpdateHost(editHost, sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to update host {0} ({1})", editHost.HostName, exc); + throw; + } + } + + public bool DeleteHost(long hostID, string sessionID) + { + try + { + return Channel.DeleteHost(hostID, sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to delete host {0} ({1})", hostID, exc); + throw; + } + } + + public List GetSessionHosts(string sessionID) + { + try + { + return Channel.GetSessionHosts(sessionID); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get hosts for session {0} ({1})", sessionID, exc); + throw; + } + } + + public EnterpriseConnectionDetails GetSessionConnectionDetails(string sessionID, long hostID, string sessionKey) + { + try + { + return Channel.GetSessionConnectionDetails(sessionID, hostID, sessionKey); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get connection details for session {0} ({1})", sessionID, exc); + throw; + } + } + + public string CreateUserSession(string sessionID, long hostID, string username, string password) + { + try + { + return Channel.CreateUserSession(sessionID,hostID,username,password); + } + catch (Exception exc) + { + Trace.TraceError("Failed to create user session ({0})", exc); + throw; + } + } + } +} \ No newline at end of file diff --git a/Myrtille.Web/src/MFAAuthenticationClient.cs b/Myrtille.Web/src/MFAAuthenticationClient.cs new file mode 100644 index 0000000..c23095e --- /dev/null +++ b/Myrtille.Web/src/MFAAuthenticationClient.cs @@ -0,0 +1,62 @@ +using System; +using System.Diagnostics; +using System.ServiceModel; +using Myrtille.Services.Contracts; + +namespace Myrtille.Web +{ + public class MFAAuthenticationClient : ClientBase, IMFAAuthentication + { + public bool GetState() + { + try + { + return Channel.GetState(); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get mfa adapter state ({0})", exc); + return false; + } + } + + public bool Authenticate(string username, string password, string clientIP = null) + { + try + { + return Channel.Authenticate(username, password, clientIP); + } + catch (Exception exc) + { + Trace.TraceError("Failed to mfa authenticate user {0} ({1})", username, exc); + return false; + } + } + + public string GetPromptLabel() + { + try + { + return Channel.GetPromptLabel(); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get mfa prompt label ({0})", exc); + return null; + } + } + + public string GetProviderURL() + { + try + { + return Channel.GetProviderURL(); + } + catch (Exception exc) + { + Trace.TraceError("Failed to get mfa provider url ({0})", exc); + return null; + } + } + } +} \ No newline at end of file diff --git a/Myrtille.Web/src/RemoteSession.cs b/Myrtille.Web/src/RemoteSession.cs index a5b2c14..e0d69e0 100644 --- a/Myrtille.Web/src/RemoteSession.cs +++ b/Myrtille.Web/src/RemoteSession.cs @@ -16,6 +16,8 @@ You may obtain a copy of the License at limitations under the License. */ +using Myrtille.Services.Contracts; + namespace Myrtille.Web { public class RemoteSession @@ -37,7 +39,9 @@ public class RemoteSession public bool StatMode; public bool DebugMode; public bool CompatibilityMode; - public string Program; + public string StartProgram; + public bool AllowRemoteClipboard; // set in myrtille web config + public SecurityProtocolEnum SecurityProtocol; public RemoteSession() { diff --git a/Myrtille.Web/src/RemoteSessionPipes.cs b/Myrtille.Web/src/RemoteSessionPipes.cs index 65fa2b6..0ea3e8c 100644 --- a/Myrtille.Web/src/RemoteSessionPipes.cs +++ b/Myrtille.Web/src/RemoteSessionPipes.cs @@ -106,7 +106,7 @@ private void InputsPipeConnected(IAsyncResult e) RemoteSession.Manager.SendCommand(RemoteSessionCommand.SendUserDomain, RemoteSession.UserDomain); RemoteSession.Manager.SendCommand(RemoteSessionCommand.SendUserName, RemoteSession.UserName); RemoteSession.Manager.SendCommand(RemoteSessionCommand.SendUserPassword, RemoteSession.UserPassword); - RemoteSession.Manager.SendCommand(RemoteSessionCommand.SendStartProgram, RemoteSession.Program); + RemoteSession.Manager.SendCommand(RemoteSessionCommand.SendStartProgram, RemoteSession.StartProgram); // send client settings, if defined (they will be otherwise send later by the client) if (RemoteSession.ImageEncoding.HasValue) diff --git a/Myrtille.Web/src/RemoteSessionProcessClient.cs b/Myrtille.Web/src/RemoteSessionProcessClient.cs index ce6260c..37dd6db 100644 --- a/Myrtille.Web/src/RemoteSessionProcessClient.cs +++ b/Myrtille.Web/src/RemoteSessionProcessClient.cs @@ -35,14 +35,20 @@ public RemoteSessionProcessClient(RemoteSessionManager remoteSessionManager, Ins public void StartProcess( int remoteSessionId, + string serverAddress, + string userDomain, + string userName, + string startProgram, int clientWidth, - int clientHeight) + int clientHeight, + bool allowRemoteClipboard, + SecurityProtocolEnum securityProtocol) { - Trace.TraceInformation("Calling service start process, remote session {0}", _remoteSessionManager.RemoteSession.Id); + Trace.TraceInformation("Calling service start process, remote session {0}, server {1}, domain {2}, user {3}, program {4}", remoteSessionId, serverAddress, string.IsNullOrEmpty(userDomain) ? "(none)" : userDomain, userName, string.IsNullOrEmpty(startProgram) ? "(none)" : startProgram); try { - Channel.StartProcess(remoteSessionId, clientWidth, clientHeight); + Channel.StartProcess(remoteSessionId, serverAddress, userDomain, userName, startProgram, clientWidth, clientHeight, allowRemoteClipboard, securityProtocol); } catch (Exception exc) { diff --git a/Myrtille.sln b/Myrtille.sln index daa548d..1a8d610 100644 --- a/Myrtille.sln +++ b/Myrtille.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Myrtille.Common", "Myrtille.Common\Myrtille.Common.csproj", "{37630774-1321-4E6A-8661-4430A8946E9E}" EndProject @@ -21,6 +21,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Myrtille.Documents", "Myrti README.md = README.md EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Myrtille.MFAProviders", "Myrtille.MFAProviders\Myrtille.MFAProviders.csproj", "{3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Myrtille.Enterprise", "Myrtille.Enterprise\Myrtille.Enterprise.csproj", "{9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|.NET = Debug|.NET @@ -119,8 +123,59 @@ Global {010E1702-3045-4B13-BFB6-06FFC60B5CBB}.Release|Win32.ActiveCfg = Release|Any CPU {010E1702-3045-4B13-BFB6-06FFC60B5CBB}.Release|x64.ActiveCfg = Release|Any CPU {010E1702-3045-4B13-BFB6-06FFC60B5CBB}.Release|x86.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|.NET.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|.NET.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Win32.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|Win32.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|x64.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|x64.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|x86.ActiveCfg = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Debug|x86.Build.0 = Debug|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|.NET.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|.NET.Build.0 = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Any CPU.Build.0 = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Win32.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|Win32.Build.0 = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|x64.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|x64.Build.0 = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|x86.ActiveCfg = Release|Any CPU + {3573C84D-5FD3-4BAC-BF96-20B9FD0709FF}.Release|x86.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|.NET.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|.NET.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Win32.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|Win32.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|x64.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|x64.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|x86.ActiveCfg = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Debug|x86.Build.0 = Debug|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|.NET.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|.NET.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Any CPU.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Win32.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|Win32.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|x64.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|x64.Build.0 = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|x86.ActiveCfg = Release|Any CPU + {9E4BF7C3-8EA3-428C-BEA3-06CC3AFCAEB4}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5A9EEADE-56B8-47F1-A860-6437A1A68F06} + EndGlobalSection EndGlobal diff --git a/README.md b/README.md index b356237..db18b94 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ More information in the DOCUMENTATION.md file. ## Features - HTTP(S) to RDP gateway +- Multifactor Authentication +- Active Directory integration - Start remote application from URL - File transfer (local and roaming accounts) - HTML4 and HTML5 support @@ -49,6 +51,8 @@ See DOCUMENTATION.md for more details. ## Usage Once Myrtille is installed on your server, you can use it at http://myserver/myrtille. Set the rdp server address, user domain (if any), name and password then click "Connect!" to log in. "Disconnect" to log out. +Multifactor Authentication and Active Directory integration (Enterprise Mode) are disabled by default. Please read documentation for activation of these features. + You can also connect a remote desktop and **start a program automatically from an url** (see DOCUMENTATION.md). From version 1.5.0, Myrtille does support encrypted credentials (aka "password 51" into .rdp files) so the urls can be distributed to third parties without compromising on security. The installer creates a self-signed certificate for https://myserver/myrtille. Like for all self-signed certificates, you will have to add a security exception into your browser (just ignore the warning message and proceed to the website). @@ -72,6 +76,7 @@ Myrtille uses the following licensed software: - WebP encoding: libWebP 0.5.1 (https://developers.google.com/speed/webp/), licensed under BSD-style Open Source license. Copyright (c) 2010, Google Inc. All rights reserved. - HTML5 websockets: Microsoft.WebSockets 0.2.3.1 (https://www.nuget.org/packages/Microsoft.WebSockets/0.2.3.1), licensed under MIT license. Copyright (c) Microsoft 2012. - Logging: Log4net 2.0.8 (https://logging.apache.org/log4net/), licensed under Apache 2.0 license. +- Multifactor Authentication: OASIS.Integration 1.6.1 (https://www.nuget.org/packages/OASIS.Integration/1.6.1), licensed under Apache 2.0 license. Source code available at https://github.com/OliveInnovations/OASIS. Copyright Olive Innovations Ltd 2017. See DISCLAIMERS.md file.