Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .ci/Scripts/buildFreeRdp.bat
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ cmd /c cmake . -B"./Build/x64" -G"Visual Studio 17 2022"^
-DWITH_CLIENT_INTERFACE=ON^
-DCHANNEL_URBDRC=OFF^
-DWITH_MEDIA_FOUNDATION=OFF^
-DZLIB_USE_STATIC_LIBS=ON^
-DZLIB_LIBRARY="%zlibDir%/Install/lib/zlib.lib"^
-DZLIB_INCLUDE_DIR="%zlibDir%/Install/include"^
-DWITH_SMARTCARD_EMULATE=OFF^
-DWITH_FFMPEG=OFF^
-DWITH_VIDEO_FFMPEG=OFF^
-DWITH_DSP_FFMPEG=OFF^
Expand Down
21 changes: 21 additions & 0 deletions UiPath.FreeRdpClient/UiPath.FreeRdpClient.Tests/RdpClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,25 @@ public async Task WrongPassword_ShouldFail()
var exception = await Connect(connectionSettings).ShouldThrowAsync<COMException>();
exception.Message.Contains("Logon Failed", StringComparison.InvariantCultureIgnoreCase);
}

//[Fact] // can't run everywhere, so leaving it commented for now
public async Task SmartCard()
{
var username = "user1";
var domain = "test.lab";
var password = "12345678";

var settings = new RdpConnectionSettings(username, domain, password)
{
IsSmartCardLogon = true,
//ReaderName = "Microsoft Virtual Smart Card 0",
//CspName = "Microsoft Base Smart Card Crypto Provider",
//ContainerName = "te-SmartcardV4-025548ff-3a4a-4c89-31544",
};

await using var _ = await Connect(settings);
_output.WriteLine("Connected, waiting 10s for session to be created...");
await Task.Delay(10_000);
_output.WriteLine("Done");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,15 @@ public async Task<IAsyncDisposable> Connect(RdpConnectionSettings connectionSett
ScopeName = connectionSettings.ScopeName,
ClientName = connectionSettings.ClientName,
HostName = connectionSettings.HostName,
Port = connectionSettings.Port ?? default
Port = connectionSettings.Port ?? default,

SmartcardSettings = new()
{
IsSmartCardLogon = connectionSettings.IsSmartCardLogon,
ReaderName = connectionSettings.ReaderName,
CspName = connectionSettings.CspName,
ContainerName = connectionSettings.ContainerName
},
};

using (await _initLock.LockAsync())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ public struct ConnectOptions
[MarshalAs(UnmanagedType.BStr)]
public string HostName;
public int Port;

public SmartcardSettings SmartcardSettings;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct SmartcardSettings
{
public bool IsSmartCardLogon;
[MarshalAs(UnmanagedType.BStr)]
public string? ReaderName;
[MarshalAs(UnmanagedType.BStr)]
public string? ContainerName;
[MarshalAs(UnmanagedType.BStr)]
public string? CspName;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ public class RdpConnectionSettings
[MaxLength(15, ErrorMessage = "Sometimes :) Windows returns only first 15 chars for a session ClientName")]
public string? ClientName { get; set; }

public bool IsSmartCardLogon { get; set; }
public string? ReaderName { get; set; }
public string? CspName { get; set; }
public string? ContainerName { get; set; }

public DisconnectCallback? DisconnectCallback { get; set; }

public RdpConnectionSettings(string username, string domain, string password)
Expand Down
174 changes: 170 additions & 4 deletions UiPath.FreeRdpClient/UiPath.FreeRdpWrapper/FreeRdpWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
#pragma warning(disable : 4324 4201 4245 4996)
#include <freerdp/freerdp.h>
#include <freerdp/cache/cache.h>
#include <freerdp/addin.h>
#include <freerdp/client/channels.h>
#include <freerdp/channels/rdpsnd.h>
#include <freerdp/channels/rdpdr.h>
#pragma warning(default : 4324 4201 4245 4996)
#pragma once
using namespace Logging;
Expand All @@ -20,6 +24,21 @@ namespace FreeRdpClient
return _strdup(convToUTF8.to_bytes(source).c_str());
}

wchar_t* ConvToUtf16(const char* source)
{
if (!source) // Handle null input
{
return nullptr;
}

std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::wstring wideString = converter.from_bytes(source);

wchar_t* wstr = new wchar_t[wideString.size() + 1];
std::wmemcpy(wstr, wideString.c_str(), wideString.size() + 1);
return wstr;
}

class instance_data
{
public:
Expand Down Expand Up @@ -98,7 +117,110 @@ namespace FreeRdpClient
return instance;
}

void PrepareRdpContext(rdpContext* context, const ConnectOptions* rdpOptions)
static BOOL LoadStaticChannelAddin(rdpChannels* channels,
rdpSettings* settings, const char* name,
void* data)
{
PVIRTUALCHANNELENTRY entry = NULL;
PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(
name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC | FREERDP_ADDIN_CHANNEL_ENTRYEX);
PVIRTUALCHANNELENTRYEX pvceex = WINPR_FUNC_PTR_CAST(pvce, PVIRTUALCHANNELENTRYEX);

if (!pvceex)
entry =
freerdp_load_channel_addin_entry(name, NULL, NULL, FREERDP_ADDIN_CHANNEL_STATIC);

if (pvceex)
{
if (freerdp_channels_client_load_ex(channels, settings, pvceex, data) == 0)
{
DT_TRACE(L"loading channelEx %s", name);
return TRUE;
}
}
else if (entry)
{
if (freerdp_channels_client_load(channels, settings, entry, data) == 0)
{
DT_TRACE(L"loading channel %s", name);
return TRUE;
}
}

return FALSE;
}

BOOL AddStaticChannel(rdpSettings* settings, size_t count, const char* const* params)
{
ADDIN_ARGV* _args = NULL;

if (!settings || !params || !params[0] || (count > INT_MAX))
return FALSE;

if (freerdp_static_channel_collection_find(settings, params[0]))
return TRUE;

_args = freerdp_addin_argv_new(count, params);

if (!_args)
return FALSE;

if (!freerdp_static_channel_collection_add(settings, _args))
{
freerdp_addin_argv_free(_args);
return FALSE;
}

return TRUE;
}

static BOOL LoadChannels(rdpChannels* channels, rdpSettings* settings)
{
if (!LoadStaticChannelAddin(channels, settings, RDPDR_SVC_CHANNEL_NAME,
settings))
return FALSE;

if (!freerdp_static_channel_collection_find(settings, RDPSND_CHANNEL_NAME))
{
const char* const params[] = { RDPSND_CHANNEL_NAME, "sys:fake" };

if (!AddStaticChannel(settings, ARRAYSIZE(params), params))
return FALSE;
}

if (freerdp_settings_get_bool(settings, FreeRDP_RedirectSmartCards))
{
if (!freerdp_device_collection_find_type(settings, RDPDR_DTYP_SMARTCARD))
{
RDPDR_DEVICE* smartcard = freerdp_device_new(RDPDR_DTYP_SMARTCARD, 0, NULL);

if (!smartcard)
return FALSE;

if (!freerdp_device_collection_add(settings, smartcard))
{
freerdp_device_free(smartcard);
return FALSE;
}
}
}

for (UINT32 i = 0; i < freerdp_settings_get_uint32(settings, FreeRDP_StaticChannelCount); i++)
{
ADDIN_ARGV* _args = static_cast<ADDIN_ARGV*>(freerdp_settings_get_pointer_array_writable(settings,
FreeRDP_StaticChannelArray, i));

if (!LoadStaticChannelAddin(channels, settings, _args->argv[0], _args))
return FALSE;
}
}

BOOL LoadChannelsCore(freerdp* instance)
{
return LoadChannels(instance->context->channels, instance->context->settings);
}

BOOL PrepareRdpContext(rdpContext* context, const ConnectOptions* rdpOptions)
{
context->settings->ServerHostname = ConvToUtf8(rdpOptions->HostName);

Expand Down Expand Up @@ -140,6 +262,51 @@ namespace FreeRdpClient
context->settings->ColorDepth = rdpOptions->Depth;

context->settings->AllowFontSmoothing = rdpOptions->FontSmoothing;

if (rdpOptions->smartcardSettings.IsSmartCardLogon)
{
context->settings->SmartcardLogon = TRUE;
context->settings->PasswordIsSmartcardPin = TRUE;
context->settings->RedirectSmartCards = TRUE;
context->settings->DeviceRedirection = TRUE;
context->settings->KeySpec = 1;

context->settings->ReaderName = ConvToUtf8(rdpOptions->smartcardSettings.ReaderName);
context->settings->CspName = ConvToUtf8(rdpOptions->smartcardSettings.CspName);
context->settings->ContainerName = ConvToUtf8(rdpOptions->smartcardSettings.ContainerName);

context->instance->LoadChannels = LoadChannelsCore;

// Only called if multiple certificates are available for the same user
context->instance->ChooseSmartcard = [](freerdp* instance,
SmartcardCertInfo** cert_list, DWORD count,
DWORD* choice, BOOL gateway) -> BOOL
{
auto res = false;
auto containerName = ConvToUtf16(instance->context->settings->ContainerName);

for (DWORD idx = 0; idx < count; idx++)
{
if (wcscmp(cert_list[idx]->containerName, containerName) != 0)
continue;

*choice = idx;
res = true;
break;
}

delete[] containerName;
return res;
};

if (freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0) != CHANNEL_RC_OK)
{
DT_ERROR(L"Failed to register addin provider");
return FALSE;
}
}

return TRUE;
}

DWORD ReleaseAll(instance_data* instanceData)
Expand Down Expand Up @@ -276,10 +443,9 @@ namespace FreeRdpClient
return E_OUTOFMEMORY;

rdpContext* context = instance->context;
PrepareRdpContext(context, rdpOptions);

auto connectResult = freerdp_connect(instance);
if (connectResult)
if (PrepareRdpContext(context, rdpOptions)
&& freerdp_connect(instance))
{
_bstr_t eventName;
if (transport_start(context, rdpOptions, eventName))
Expand Down
10 changes: 10 additions & 0 deletions UiPath.FreeRdpClient/UiPath.FreeRdpWrapper/FreeRdpWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

namespace FreeRdpClient
{
typedef struct
{
BOOL IsSmartCardLogon;
BSTR ReaderName;
BSTR ContainerName;
BSTR CspName;
} SmartcardSettings;

typedef struct
{
long Width;
Expand All @@ -17,6 +25,8 @@ namespace FreeRdpClient
BSTR ClientName;
BSTR HostName;
long Port;

SmartcardSettings smartcardSettings;
} ConnectOptions;

using pFreeRdpDisconnectedCallback = void (*)(BSTR);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>comsuppw.lib;comsupp.lib;freerdp3.lib;winpr3.lib;libcrypto.lib;libssl.lib;Dbghelp.lib;Secur32.lib;ntdsapi.lib;Rpcrt4.lib;Crypt32.lib;ncrypt.lib;Userenv.lib;ws2_32.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\OpenSSL-VC-$(PlatformArchitecture)\lib;..\..\Build\$(PlatformTarget)\libfreerdp\$(Configuration);..\..\Build\$(PlatformTarget)\winpr\libwinpr\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalDependencies>comsuppw.lib;comsupp.lib;freerdp3.lib;freerdp-client3.lib;winpr3.lib;remdesk-common.lib;winmm.lib;libcrypto.lib;libssl.lib;Dbghelp.lib;Secur32.lib;ntdsapi.lib;Rpcrt4.lib;Crypt32.lib;ncrypt.lib;Userenv.lib;ws2_32.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\OpenSSL-VC-$(PlatformArchitecture)\lib;..\..\Build\$(PlatformTarget)\libfreerdp\$(Configuration);..\..\Build\$(PlatformTarget)\winpr\libwinpr\$(Configuration);..\..\Build\$(PlatformTarget)\client\common\$(Configuration);..\..\Build\$(PlatformTarget)\channels\remdesk\common\$(Configuration)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>
</ModuleDefinitionFile>
</Link>
Expand Down Expand Up @@ -102,8 +102,8 @@
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>comsuppw.lib;comsupp.lib;freerdp3.lib;winpr3.lib;libcrypto.lib;libssl.lib;Dbghelp.lib;Secur32.lib;ntdsapi.lib;Rpcrt4.lib;Crypt32.lib;ncrypt.lib;Userenv.lib;ws2_32.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\OpenSSL-VC-$(PlatformArchitecture)\lib;..\..\Build\$(PlatformTarget)\libfreerdp\$(Configuration);..\..\Build\$(PlatformTarget)\winpr\libwinpr\$(Configuration)</AdditionalLibraryDirectories>
<AdditionalDependencies>comsuppw.lib;comsupp.lib;freerdp3.lib;freerdp-client3.lib;winpr3.lib;remdesk-common.lib;winmm.lib;libcrypto.lib;libssl.lib;Dbghelp.lib;Secur32.lib;ntdsapi.lib;Rpcrt4.lib;Crypt32.lib;ncrypt.lib;Userenv.lib;ws2_32.lib;Shlwapi.lib;ntdll.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\OpenSSL-VC-$(PlatformArchitecture)\lib;..\..\Build\$(PlatformTarget)\libfreerdp\$(Configuration);..\..\Build\$(PlatformTarget)\winpr\libwinpr\$(Configuration);..\..\Build\$(PlatformTarget)\client\common\$(Configuration);..\..\Build\$(PlatformTarget)\channels\remdesk\common\$(Configuration)</AdditionalLibraryDirectories>
<ModuleDefinitionFile>
</ModuleDefinitionFile>
</Link>
Expand Down
Loading