diff --git a/mRemoteNG/Connection/AbstractConnectionRecord.cs b/mRemoteNG/Connection/AbstractConnectionRecord.cs
index 4ce1faf3b..7d3dd58e5 100644
--- a/mRemoteNG/Connection/AbstractConnectionRecord.cs
+++ b/mRemoteNG/Connection/AbstractConnectionRecord.cs
@@ -58,6 +58,7 @@ namespace mRemoteNG.Connection
private string _rdGatewayUsername;
private string _rdGatewayPassword;
private string _rdGatewayDomain;
+ private string _rdGatewayAccessToken;
private ExternalCredentialProvider _rdGatewayExternalCredentialProvider;
private string _rdGatewayUserViaAPI = "";
@@ -530,6 +531,17 @@ namespace mRemoteNG.Connection
set => SetField(ref _rdGatewayPassword, value, "RDGatewayPassword");
}
+ [LocalizedAttributes.LocalizedCategory(nameof(Language.RDPGateway), 4),
+ LocalizedAttributes.LocalizedDisplayName(nameof(Language.RdpGatewayAccessToken)),
+ LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRdpGatewayAccessToken)),
+ PasswordPropertyText(true),
+ AttributeUsedInProtocol(ProtocolType.RDP)]
+ public string RDGatewayAccessToken
+ {
+ get => GetPropertyValue("RDGatewayAccessToken", _rdGatewayAccessToken);
+ set => SetField(ref _rdGatewayAccessToken, value, "RDGatewayAccessToken");
+ }
+
[LocalizedAttributes.LocalizedCategory(nameof(Language.RDPGateway), 4),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.RdpGatewayDomain)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionRDGatewayDomain)),
diff --git a/mRemoteNG/Connection/Protocol/RDP/RDGatewayUseConnectionCredentials.cs b/mRemoteNG/Connection/Protocol/RDP/RDGatewayUseConnectionCredentials.cs
index e46d35352..5ef2e315a 100644
--- a/mRemoteNG/Connection/Protocol/RDP/RDGatewayUseConnectionCredentials.cs
+++ b/mRemoteNG/Connection/Protocol/RDP/RDGatewayUseConnectionCredentials.cs
@@ -15,6 +15,9 @@ namespace mRemoteNG.Connection.Protocol.RDP
SmartCard = 2,
[LocalizedAttributes.LocalizedDescription(nameof(Language.UseExternalCredentialProvider))]
- ExternalCredentialProvider = 3
+ ExternalCredentialProvider = 3,
+
+ [LocalizedAttributes.LocalizedDescription(nameof(Language.UseAccessToken))]
+ AccessToken = 4
}
}
\ No newline at end of file
diff --git a/mRemoteNG/Connection/Protocol/RDP/RdGatewayAccessTokenHelper.cs b/mRemoteNG/Connection/Protocol/RDP/RdGatewayAccessTokenHelper.cs
new file mode 100644
index 000000000..4af75a581
--- /dev/null
+++ b/mRemoteNG/Connection/Protocol/RDP/RdGatewayAccessTokenHelper.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+
+
+namespace mRemoteNG.Connection.Protocol.RDP
+{
+ internal static class RdGatewayAccessTokenHelper
+ {
+ public static string EncryptAuthCookieString(string cookieString)
+ {
+ byte[] cookieBytes = TsCryptEncryptString(cookieString);
+
+ if (cookieBytes != null)
+ {
+ return Convert.ToBase64String(cookieBytes);
+ }
+
+ return null;
+ }
+
+ public static string DecryptAuthCookieString(string cookieString) //TODO: decrypt is newer use, should we remove it?
+ {
+ return TsCryptDecryptString(Convert.FromBase64String(cookieString));
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct CryptProtectPromptStruct
+ {
+ public int Size;
+ public int Flags;
+ public IntPtr Window;
+ public string Message;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct DataBlob
+ {
+ public int Size;
+ public IntPtr Data;
+ }
+
+ private const int CRYPTPROTECT_LOCAL_MACHINE = 0x00000004;
+ private const int CRYPTPROTECT_UI_FORBIDDEN = 0x00000001;
+ private const int CRYPTPROTECT_AUDIT = 0x00000010;
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Unicode)]
+ private static extern bool CryptProtectData(
+ ref DataBlob dataIn,
+ IntPtr description,
+ IntPtr optionalEntropy,
+ IntPtr reserved,
+ IntPtr promptStruct,
+ int flags,
+ out DataBlob dataOut);
+
+ [DllImport("crypt32.dll", CharSet = CharSet.Unicode)] //TODO: decrypt is newer use, should we remove it?
+ private static extern bool CryptUnprotectData(
+ ref DataBlob dataIn,
+ IntPtr description,
+ IntPtr optionalEntropy,
+ IntPtr reserved,
+ IntPtr promptStruct,
+ int flags,
+ out DataBlob dataOut);
+
+ private static byte[] TsCryptEncryptString(string inputString)
+ {
+ DataBlob inputBlob;
+ DataBlob outputBlob;
+ byte[] outputData = null;
+
+ byte[] stringBytes = Encoding.Unicode.GetBytes(inputString);
+ byte[] inputData = new byte[stringBytes.Length + 2];
+ Buffer.BlockCopy(stringBytes, 0, inputData, 0, stringBytes.Length);
+
+ inputBlob.Size = inputData.Length;
+ inputBlob.Data = Marshal.AllocHGlobal(inputData.Length);
+ Marshal.Copy(inputData, 0, inputBlob.Data, inputBlob.Size);
+
+ if (CryptProtectData(ref inputBlob, IntPtr.Zero, IntPtr.Zero,
+ IntPtr.Zero, IntPtr.Zero, CRYPTPROTECT_UI_FORBIDDEN, out outputBlob))
+ {
+ outputData = new byte[outputBlob.Size];
+ Marshal.Copy(outputBlob.Data, outputData, 0, outputBlob.Size);
+ }
+
+ Marshal.FreeHGlobal(inputBlob.Data);
+ Marshal.FreeHGlobal(outputBlob.Data);
+
+ return outputData;
+ }
+
+ private static string TsCryptDecryptString(byte[] inputBytes) //TODO: decrypt is newer use, should we remove it?
+ {
+ DataBlob inputBlob;
+ DataBlob outputBlob;
+ byte[] outputData = null;
+
+ inputBlob.Size = inputBytes.Length;
+ inputBlob.Data = Marshal.AllocHGlobal(inputBytes.Length);
+ Marshal.Copy(inputBytes, 0, inputBlob.Data, inputBlob.Size);
+
+ if (CryptUnprotectData(ref inputBlob, IntPtr.Zero, IntPtr.Zero,
+ IntPtr.Zero, IntPtr.Zero, CRYPTPROTECT_UI_FORBIDDEN, out outputBlob))
+ {
+ outputData = new byte[outputBlob.Size];
+ Marshal.Copy(outputBlob.Data, outputData, 0, outputBlob.Size);
+ }
+
+ Marshal.FreeHGlobal(inputBlob.Data);
+ Marshal.FreeHGlobal(outputBlob.Data);
+
+ if (outputData != null)
+ {
+ return Encoding.Unicode.GetString(outputData).TrimEnd((Char)0);
+ }
+
+ return null;
+ }
+
+ }
+}
diff --git a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs
index 144695aa3..f4eb39537 100644
--- a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs
+++ b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol.cs
@@ -428,9 +428,18 @@ namespace mRemoteNG.Connection.Protocol.RDP
}
}
- _rdpClient.TransportSettings2.GatewayUsername = gwu;
- _rdpClient.TransportSettings2.GatewayPassword = gwp;
- _rdpClient.TransportSettings2.GatewayDomain = gwd;
+
+ if (connectionInfo.RDGatewayUseConnectionCredentials != RDGatewayUseConnectionCredentials.AccessToken)
+ {
+ _rdpClient.TransportSettings2.GatewayUsername = gwu;
+ _rdpClient.TransportSettings2.GatewayPassword = gwp;
+ _rdpClient.TransportSettings2.GatewayDomain = gwd;
+ }
+ else
+ {
+ //TODO: should we check client version and throw if it is less than 7
+ }
+
break;
}
}
diff --git a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol7.cs b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol7.cs
index 9ff0ba0ba..d08c13267 100644
--- a/mRemoteNG/Connection/Protocol/RDP/RdpProtocol7.cs
+++ b/mRemoteNG/Connection/Protocol/RDP/RdpProtocol7.cs
@@ -37,6 +37,15 @@ namespace mRemoteNG.Connection.Protocol.RDP
if (connectionInfo.UseEnhancedMode)
RdpClient7.AdvancedSettings7.PCB += ";EnhancedMode=1";
}
+
+ if (connectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.AccessToken)
+ {
+ var authToken = connectionInfo.RDGatewayAccessToken;
+ var encryptedAuthToken = RdGatewayAccessTokenHelper.EncryptAuthCookieString(authToken);
+ RdpClient7.TransportSettings3.GatewayEncryptedAuthCookie = encryptedAuthToken;
+ RdpClient7.TransportSettings3.GatewayEncryptedAuthCookieSize = (uint)encryptedAuthToken.Length;
+ RdpClient7.TransportSettings3.GatewayCredsSource = 5;
+ }
}
catch (Exception ex)
{
diff --git a/mRemoteNG/Language/Language.Designer.cs b/mRemoteNG/Language/Language.Designer.cs
index 8733b2f01..bbca400d4 100644
--- a/mRemoteNG/Language/Language.Designer.cs
+++ b/mRemoteNG/Language/Language.Designer.cs
@@ -646,7 +646,7 @@ namespace mRemoteNG.Resources.Language {
}
///
- /// Looks up a localized string similar to Automatically try to reconnect when disconnected from server (RDP && ICA only).
+ /// Looks up a localized string similar to Display reconnection dialog when disconnected from server (RDP && ICA only).
///
internal static string CheckboxAutomaticReconnect {
get {
@@ -663,6 +663,15 @@ namespace mRemoteNG.Resources.Language {
}
}
+ ///
+ /// Looks up a localized string similar to Automatically try to reconnect when disconnected from server (RDP && ICA only).
+ ///
+ internal static string CheckboxNoReconnect {
+ get {
+ return ResourceManager.GetString("CheckboxNoReconnect", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to This proxy server requires authentication.
///
@@ -1547,7 +1556,7 @@ namespace mRemoteNG.Resources.Language {
}
///
- /// Looks up a localized string similar to Disable Cursor blinking.
+ /// Looks up a localized string similar to Disable Cursor Blinking.
///
internal static string DisableCursorBlinking {
get {
@@ -4053,6 +4062,15 @@ namespace mRemoteNG.Resources.Language {
}
}
+ ///
+ /// Looks up a localized string similar to Specifies the access token for Remote Desktop Gateway server..
+ ///
+ internal static string PropertyDescriptionRdpGatewayAccessToken {
+ get {
+ return ResourceManager.GetString("PropertyDescriptionRdpGatewayAccessToken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Specifies the password of the Remote Desktop Gateway server..
///
@@ -4133,8 +4151,8 @@ namespace mRemoteNG.Resources.Language {
return ResourceManager.GetString("PropertyDescriptionRedirectDiskDrivesCustom", resourceCulture);
}
}
-
- ///
+
+ ///
/// Looks up a localized string similar to Select whether local disk drives should be shown on the remote host..
///
internal static string PropertyDescriptionRedirectDrives {
@@ -4909,6 +4927,15 @@ namespace mRemoteNG.Resources.Language {
}
}
+ ///
+ /// Looks up a localized string similar to RDP-Gateway Access Token.
+ ///
+ internal static string RdpGatewayAccessToken {
+ get {
+ return ResourceManager.GetString("RdpGatewayAccessToken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to RDP-Gateway Domain.
///
@@ -6334,6 +6361,15 @@ namespace mRemoteNG.Resources.Language {
}
}
+ ///
+ /// Looks up a localized string similar to Use RD Gateway access token.
+ ///
+ internal static string UseAccessToken {
+ get {
+ return ResourceManager.GetString("UseAccessToken", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Use Console Session.
///
diff --git a/mRemoteNG/Language/Language.resx b/mRemoteNG/Language/Language.resx
index 6e9c76e84..9c6c99bdd 100644
--- a/mRemoteNG/Language/Language.resx
+++ b/mRemoteNG/Language/Language.resx
@@ -2334,4 +2334,13 @@ Nightly Channel includes Alphas, Betas & Release Candidates.
Custom Drives
+
+ Specifies the access token for Remote Desktop Gateway server.
+
+
+ RDP-Gateway Access Token
+
+
+ Use RD Gateway access token
+
\ No newline at end of file
diff --git a/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs b/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs
index 3757bf14d..2a1d1faf3 100644
--- a/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs
+++ b/mRemoteNG/UI/Controls/ConnectionInfoPropertyGrid/ConnectionInfoPropertyGrid.cs
@@ -267,6 +267,7 @@ namespace mRemoteNG.UI.Controls.ConnectionInfoPropertyGrid
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayPassword));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUseConnectionCredentials));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUsername));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayAccessToken));
}
else if (SelectedConnectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.Yes ||
SelectedConnectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.SmartCard)
@@ -276,12 +277,22 @@ namespace mRemoteNG.UI.Controls.ConnectionInfoPropertyGrid
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUsername));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayExternalCredentialProvider));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUserViaAPI));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayAccessToken));
}
else if (SelectedConnectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.ExternalCredentialProvider)
{
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayDomain));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayPassword));
strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUsername));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayAccessToken));
+ }
+ else if (SelectedConnectionInfo.RDGatewayUseConnectionCredentials == RDGatewayUseConnectionCredentials.AccessToken)
+ {
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayDomain));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayPassword));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUsername));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayExternalCredentialProvider));
+ strHide.Add(nameof(AbstractConnectionRecord.RDGatewayUserViaAPI));
}
if (!(SelectedConnectionInfo.Resolution == RDPResolutions.FitToWindow ||