From 3c18a9980f8136f7a0b0c6e1b83f7830895a01ab Mon Sep 17 00:00:00 2001 From: David Fort Date: Fri, 22 Sep 2023 22:13:54 +0200 Subject: [PATCH] [client,win32] implement connection to child session Under windows you can connect to a child session by requesting a named pipe to the local server, and then do some RDP on this named pipe. The protocol is like for /vmconnect with CredSSP, then Nego and then the "normal" workflow for a connection. For CredSSP we force the usage of NTLM for the Negociate SSPI, and the credentials are empty. --- client/common/cmdline.c | 17 +- client/common/cmdline.h | 4 + include/freerdp/settings.h | 86 ++--- libfreerdp/common/settings_getters.c | 7 + libfreerdp/common/settings_str.c | 1 + libfreerdp/core/CMakeLists.txt | 1 + libfreerdp/core/childsession.c | 340 ++++++++++++++++++ libfreerdp/core/childsession.h | 27 ++ libfreerdp/core/connection.c | 2 +- libfreerdp/core/nego.c | 43 +-- libfreerdp/core/nego.h | 1 + libfreerdp/core/tcp.h | 3 + .../core/test/settings_property_lists.h | 1 + libfreerdp/core/transport.c | 13 + libfreerdp/core/transport.h | 2 + libfreerdp/core/utils.c | 3 + 16 files changed, 482 insertions(+), 69 deletions(-) create mode 100644 libfreerdp/core/childsession.c create mode 100644 libfreerdp/core/childsession.h diff --git a/client/common/cmdline.c b/client/common/cmdline.c index a38f593d5..9eadba504 100644 --- a/client/common/cmdline.c +++ b/client/common/cmdline.c @@ -1971,7 +1971,7 @@ static int parse_gfx_options(rdpSettings* settings, const COMMAND_LINE_ARGUMENT_ WINPR_ASSERT(settings); WINPR_ASSERT(arg); - if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, TRUE)) + if (!freerdp_settings_set_bool(settings, FreeRDP_SupportGraphicsPipeline, (arg->Value != NULL))) return COMMAND_LINE_ERROR; if (arg->Value) @@ -3794,6 +3794,21 @@ static int freerdp_client_settings_parse_command_line_arguments_int(rdpSettings* settings->SendPreconnectionPdu = TRUE; settings->PreconnectionId = (UINT32)val; } +#ifdef _WIN32 + CommandLineSwitchCase(arg, "connect-child-session") + { + if (!freerdp_settings_set_string(settings, FreeRDP_AuthenticationServiceClass, + "vs-debug") || + !freerdp_settings_set_string(settings, FreeRDP_ServerHostname, "localhost") || + !freerdp_settings_set_string(settings, FreeRDP_AuthenticationPackageList, "ntlm") || + !freerdp_settings_set_bool(settings, FreeRDP_NegotiateSecurityLayer, FALSE) || + !freerdp_settings_set_bool(settings, FreeRDP_VmConnectMode, TRUE) || + !freerdp_settings_set_bool(settings, FreeRDP_ConnectChildSession, TRUE) || + !freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, TRUE) || + !freerdp_settings_set_uint32(settings, FreeRDP_AuthenticationLevel, 0)) + return COMMAND_LINE_ERROR_MEMORY; + } +#endif CommandLineSwitchCase(arg, "sec") { size_t count = 0, x; diff --git a/client/common/cmdline.h b/client/common/cmdline.h index c7e841e44..f6e0e71ea 100644 --- a/client/common/cmdline.h +++ b/client/common/cmdline.h @@ -111,6 +111,10 @@ static const COMMAND_LINE_ARGUMENT_A global_cmd_args[] = { "[DEPRECATED, use /cert:name:] Certificate name" }, { "cert-tofu", COMMAND_LINE_VALUE_FLAG, NULL, NULL, NULL, -1, NULL, "[DEPRECATED, use /cert:tofu] Automatically accept certificate on first connect" }, +#endif +#ifdef _WIN32 + { "connect-child-session", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, "", + "connect to child session (win32)" }, #endif { "client-build-number", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, "Client Build Number sent to server (influences smartcard behaviour, see [MS-RDPESC])" }, diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index eee3b50e8..beabac20c 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -485,12 +485,12 @@ extern "C" /* ThreadingFlags */ #define THREADING_FLAGS_DISABLE_THREADS 0x00000001 -/* Settings */ + /* Settings */ -/** - * FreeRDP Settings Ids - * This is generated with a script parsing the rdpSettings data structure - */ + /** + * FreeRDP Settings Ids + * This is generated with a script parsing the rdpSettings data structure + */ #define FreeRDP_instance (0) #define FreeRDP_ServerMode (16) @@ -737,6 +737,7 @@ extern "C" #define FreeRDP_OldLicenseBehaviour (1606) #define FreeRDP_MouseUseRelativeMove (1607) #define FreeRDP_UseCommonStdioCallbacks (1608) +#define FreeRDP_ConnectChildSession (1609) #define FreeRDP_ComputerName (1664) #define FreeRDP_ConnectionFile (1728) #define FreeRDP_AssistanceFile (1729) @@ -941,9 +942,9 @@ extern "C" #define FreeRDP_Floatbar (5196) #define FreeRDP_TcpConnectTimeout (5197) -/** - * FreeRDP Settings Data Structure - */ + /** + * FreeRDP Settings Data Structure + */ #define FreeRDP_Settings_StableAPI_MAX 5312 struct rdp_settings @@ -1297,15 +1298,16 @@ extern "C" UINT64 padding1601[1601 - 1560]; /* 1560 */ /* Miscellaneous */ - ALIGN64 BOOL SoftwareGdi; /* 1601 */ - ALIGN64 BOOL LocalConnection; /* 1602 */ - ALIGN64 BOOL AuthenticationOnly; /* 1603 */ - ALIGN64 BOOL CredentialsFromStdin; /* 1604 */ - ALIGN64 BOOL UnmapButtons; /* 1605 */ - ALIGN64 BOOL OldLicenseBehaviour; /* 1606 */ - ALIGN64 BOOL MouseUseRelativeMove; /* 1607 */ + ALIGN64 BOOL SoftwareGdi; /* 1601 */ + ALIGN64 BOOL LocalConnection; /* 1602 */ + ALIGN64 BOOL AuthenticationOnly; /* 1603 */ + ALIGN64 BOOL CredentialsFromStdin; /* 1604 */ + ALIGN64 BOOL UnmapButtons; /* 1605 */ + ALIGN64 BOOL OldLicenseBehaviour; /* 1606 */ + ALIGN64 BOOL MouseUseRelativeMove; /* 1607 */ ALIGN64 BOOL UseCommonStdioCallbacks; /* 1608 */ - UINT64 padding1664[1664 - 1609]; /* 1609 */ + ALIGN64 BOOL ConnectChildSession; /* 1609 */ + UINT64 padding1664[1664 - 1610]; /* 1610 */ /* Names */ ALIGN64 char* ComputerName; /* 1664 */ @@ -1339,31 +1341,31 @@ extern "C" */ /* Gateway */ - ALIGN64 UINT32 GatewayUsageMethod; /* 1984 */ - ALIGN64 UINT32 GatewayPort; /* 1985 */ - ALIGN64 char* GatewayHostname; /* 1986 */ - ALIGN64 char* GatewayUsername; /* 1987 */ - ALIGN64 char* GatewayPassword; /* 1988 */ - ALIGN64 char* GatewayDomain; /* 1989 */ - ALIGN64 UINT32 GatewayCredentialsSource; /* 1990 */ - ALIGN64 BOOL GatewayUseSameCredentials; /* 1991 */ - ALIGN64 BOOL GatewayEnabled; /* 1992 */ - ALIGN64 BOOL GatewayBypassLocal; /* 1993 */ - ALIGN64 BOOL GatewayRpcTransport; /* 1994 */ - ALIGN64 BOOL GatewayHttpTransport; /* 1995 */ - ALIGN64 BOOL GatewayUdpTransport; /* 1996 */ - ALIGN64 char* GatewayAccessToken; /* 1997 */ - ALIGN64 char* GatewayAcceptedCert; /* 1998 */ - ALIGN64 UINT32 GatewayAcceptedCertLength; /* 1999 */ - ALIGN64 BOOL GatewayHttpUseWebsockets; /* 2000 */ - ALIGN64 BOOL GatewayHttpExtAuthSspiNtlm; /* 2001 */ - ALIGN64 char* GatewayHttpExtAuthBearer; /* 2002 */ - ALIGN64 char* GatewayUrl; /* 2003 */ - ALIGN64 BOOL GatewayArmTransport; /* 2004 */ - ALIGN64 char* GatewayAvdWvdEndpointPool; /* 2005 */ - ALIGN64 char* GatewayAvdGeo; /* 2006 */ - ALIGN64 char* GatewayAvdArmpath; /* 2007 */ - ALIGN64 char* GatewayAvdAadtenantid; /* 2008 */ + ALIGN64 UINT32 GatewayUsageMethod; /* 1984 */ + ALIGN64 UINT32 GatewayPort; /* 1985 */ + ALIGN64 char* GatewayHostname; /* 1986 */ + ALIGN64 char* GatewayUsername; /* 1987 */ + ALIGN64 char* GatewayPassword; /* 1988 */ + ALIGN64 char* GatewayDomain; /* 1989 */ + ALIGN64 UINT32 GatewayCredentialsSource; /* 1990 */ + ALIGN64 BOOL GatewayUseSameCredentials; /* 1991 */ + ALIGN64 BOOL GatewayEnabled; /* 1992 */ + ALIGN64 BOOL GatewayBypassLocal; /* 1993 */ + ALIGN64 BOOL GatewayRpcTransport; /* 1994 */ + ALIGN64 BOOL GatewayHttpTransport; /* 1995 */ + ALIGN64 BOOL GatewayUdpTransport; /* 1996 */ + ALIGN64 char* GatewayAccessToken; /* 1997 */ + ALIGN64 char* GatewayAcceptedCert; /* 1998 */ + ALIGN64 UINT32 GatewayAcceptedCertLength; /* 1999 */ + ALIGN64 BOOL GatewayHttpUseWebsockets; /* 2000 */ + ALIGN64 BOOL GatewayHttpExtAuthSspiNtlm; /* 2001 */ + ALIGN64 char* GatewayHttpExtAuthBearer; /* 2002 */ + ALIGN64 char* GatewayUrl; /* 2003 */ + ALIGN64 BOOL GatewayArmTransport; /* 2004 */ + ALIGN64 char* GatewayAvdWvdEndpointPool; /* 2005 */ + ALIGN64 char* GatewayAvdGeo; /* 2006 */ + ALIGN64 char* GatewayAvdArmpath; /* 2007 */ + ALIGN64 char* GatewayAvdAadtenantid; /* 2008 */ ALIGN64 char* GatewayAvdDiagnosticserviceurl; /* 2009 */ ALIGN64 char* GatewayAvdHubdiscoverygeourl; /* 2010 */ ALIGN64 char* GatewayAvdActivityhint; /* 2011 */ @@ -1646,7 +1648,7 @@ extern "C" * Other Redirection */ - ALIGN64 BOOL RedirectClipboard; /* 4800 */ + ALIGN64 BOOL RedirectClipboard; /* 4800 */ ALIGN64 UINT32 ClipboardFeatureMask; /* 4801 */ UINT64 padding4928[4928 - 4802]; /* 4802 */ diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 665c3caee..10cac38a9 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -123,6 +123,9 @@ BOOL freerdp_settings_get_bool(const rdpSettings* settings, size_t id) case FreeRDP_CompressionEnabled: return settings->CompressionEnabled; + case FreeRDP_ConnectChildSession: + return settings->ConnectChildSession; + case FreeRDP_ConsoleSession: return settings->ConsoleSession; @@ -717,6 +720,10 @@ BOOL freerdp_settings_set_bool(rdpSettings* settings, size_t id, BOOL val) settings->CompressionEnabled = cnv.c; break; + case FreeRDP_ConnectChildSession: + settings->ConnectChildSession = cnv.c; + break; + case FreeRDP_ConsoleSession: settings->ConsoleSession = cnv.c; break; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index e6fa4bf6f..cd72e8695 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -56,6 +56,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_CertificateCallbackPreferPEM, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_CertificateCallbackPreferPEM" }, { FreeRDP_CompressionEnabled, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_CompressionEnabled" }, + { FreeRDP_ConnectChildSession, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_ConnectChildSession" }, { FreeRDP_ConsoleSession, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_ConsoleSession" }, { FreeRDP_CredentialsFromStdin, FREERDP_SETTINGS_TYPE_BOOL, "FreeRDP_CredentialsFromStdin" }, { FreeRDP_DeactivateClientDecoding, FREERDP_SETTINGS_TYPE_BOOL, diff --git a/libfreerdp/core/CMakeLists.txt b/libfreerdp/core/CMakeLists.txt index a2d23daeb..08b2e9a5f 100644 --- a/libfreerdp/core/CMakeLists.txt +++ b/libfreerdp/core/CMakeLists.txt @@ -107,6 +107,7 @@ set(${MODULE_PREFIX}_SRCS multitransport.h timezone.c timezone.h + childsession.c rdp.c rdp.h tcp.c diff --git a/libfreerdp/core/childsession.c b/libfreerdp/core/childsession.c new file mode 100644 index 000000000..082306566 --- /dev/null +++ b/libfreerdp/core/childsession.c @@ -0,0 +1,340 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Named pipe transport + * + * Copyright 2023 David Fort + * + * 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. + */ + +#include "tcp.h" +#include +#include +#include "childsession.h" + +#define TAG FREERDP_TAG("childsession") + +typedef struct +{ + HANDLE hFile; +} WINPR_BIO_NAMED; + +static int transport_bio_named_uninit(BIO* bio); + +static int transport_bio_named_write(BIO* bio, const char* buf, int size) +{ + WINPR_ASSERT(bio); + WINPR_ASSERT(buf); + + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + if (!buf) + return 0; + + BIO_clear_flags(bio, BIO_FLAGS_WRITE); + DWORD written = 0; + + BOOL ret = WriteFile(ptr->hFile, buf, size, &written, NULL); + WLog_VRB(TAG, "transport_bio_named_write(%d)=%d written=%d", size, ret, written); + + if (!ret) + return -1; + + if (written == 0) + return -1; + + return written; +} + +static int transport_bio_named_read(BIO* bio, char* buf, int size) +{ + WINPR_ASSERT(bio); + WINPR_ASSERT(buf); + + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + if (!buf) + return 0; + + BIO_clear_flags(bio, BIO_FLAGS_READ); + + DWORD readBytes; + BOOL ret = ReadFile(ptr->hFile, buf, size, &readBytes, NULL); + WLog_VRB(TAG, "transport_bio_named_read(%d)=%d read=%d", size, ret, readBytes); + if (!ret) + { + if (GetLastError() == ERROR_NO_DATA) + BIO_set_flags(bio, (BIO_FLAGS_SHOULD_RETRY | BIO_FLAGS_READ)); + return -1; + } + + if (readBytes == 0) + { + BIO_set_flags(bio, (BIO_FLAGS_READ | BIO_FLAGS_SHOULD_RETRY)); + return 0; + } + + return readBytes; +} + +static int transport_bio_named_puts(BIO* bio, const char* str) +{ + WINPR_ASSERT(bio); + WINPR_ASSERT(str); + + return transport_bio_named_write(bio, str, strlen(str)); +} + +static int transport_bio_named_gets(BIO* bio, char* str, int size) +{ + WINPR_ASSERT(bio); + WINPR_ASSERT(str); + + return transport_bio_named_write(bio, str, size); +} + +static long transport_bio_named_ctrl(BIO* bio, int cmd, long arg1, void* arg2) +{ + WINPR_ASSERT(bio); + + int status = -1; + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + switch (cmd) + { + case BIO_C_SET_SOCKET: + case BIO_C_GET_SOCKET: + return -1; + case BIO_C_GET_EVENT: + if (!BIO_get_init(bio) || !arg2) + return 0; + + *((HANDLE*)arg2) = ptr->hFile; + return 1; + case BIO_C_SET_HANDLE: + BIO_set_init(bio, 1); + if (!BIO_get_init(bio) || !arg2) + return 0; + + ptr->hFile = (HANDLE)arg2; + return 1; + case BIO_C_SET_NONBLOCK: + { + return 1; + } + case BIO_C_WAIT_READ: + { + int timeout = (int)arg1; + return 1; + } + break; + case BIO_C_WAIT_WRITE: + { + int timeout = (int)arg1; + return 1; + } + break; + default: + break; + } + + switch (cmd) + { + case BIO_CTRL_GET_CLOSE: + status = BIO_get_shutdown(bio); + break; + + case BIO_CTRL_SET_CLOSE: + BIO_set_shutdown(bio, (int)arg1); + status = 1; + break; + + case BIO_CTRL_DUP: + status = 1; + break; + + case BIO_CTRL_FLUSH: + status = 1; + break; + + default: + status = 0; + break; + } + + return status; +} + +static int transport_bio_named_uninit(BIO* bio) +{ + WINPR_ASSERT(bio); + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + if (ptr && ptr->hFile) + { + CloseHandle(ptr->hFile); + ptr->hFile = NULL; + } + + BIO_set_init(bio, 0); + BIO_set_flags(bio, 0); + return 1; +} + +static int transport_bio_named_new(BIO* bio) +{ + WINPR_ASSERT(bio); + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); + + WINPR_BIO_NAMED* ptr = (WINPR_BIO_NAMED*)calloc(1, sizeof(WINPR_BIO_NAMED)); + if (!ptr) + return 0; + + BIO_set_data(bio, ptr); + BIO_set_flags(bio, BIO_FLAGS_SHOULD_RETRY); + return 1; +} + +static int transport_bio_named_free(BIO* bio) +{ + WINPR_BIO_NAMED* ptr; + + if (!bio) + return 0; + + transport_bio_named_uninit(bio); + ptr = (WINPR_BIO_NAMED*)BIO_get_data(bio); + + if (ptr) + { + BIO_set_data(bio, NULL); + free(ptr); + } + + return 1; +} + +BIO_METHOD* BIO_s_namedpipe(void) +{ + static BIO_METHOD* bio_methods = NULL; + + if (bio_methods == NULL) + { + if (!(bio_methods = BIO_meth_new(BIO_TYPE_NAMEDPIPE, "NamedPipe"))) + return NULL; + + BIO_meth_set_write(bio_methods, transport_bio_named_write); + BIO_meth_set_read(bio_methods, transport_bio_named_read); + BIO_meth_set_puts(bio_methods, transport_bio_named_puts); + BIO_meth_set_gets(bio_methods, transport_bio_named_gets); + BIO_meth_set_ctrl(bio_methods, transport_bio_named_ctrl); + BIO_meth_set_create(bio_methods, transport_bio_named_new); + BIO_meth_set_destroy(bio_methods, transport_bio_named_free); + } + + return bio_methods; +} + +typedef NTSTATUS (*WinStationCreateChildSessionTransportFn)(WCHAR* path, DWORD len); +BOOL createChildSessionTransport(HANDLE* pFile) +{ + WINPR_ASSERT(pFile); + + HANDLE hModule = NULL; + BOOL ret = FALSE; + *pFile = INVALID_HANDLE_VALUE; + + BOOL childEnabled; + if (!WTSIsChildSessionsEnabled(&childEnabled)) + { + WLog_ERR(TAG, "error when calling WTSIsChildSessionsEnabled"); + goto out; + } + + if (!childEnabled) + { + WLog_INFO(TAG, "child sessions aren't enabled"); + if (!WTSEnableChildSessions(TRUE)) + { + WLog_ERR(TAG, "error when calling WTSEnableChildSessions"); + goto out; + } + WLog_INFO(TAG, "successfully enabled child sessions"); + } + + hModule = LoadLibraryA("winsta.dll"); + if (!hModule) + return FALSE; + WCHAR pipePath[0x80] = { 0 }; + char pipePathA[0x80] = { 0 }; + + WinStationCreateChildSessionTransportFn createChildSessionFn = + (WinStationCreateChildSessionTransportFn)GetProcAddress( + hModule, "WinStationCreateChildSessionTransport"); + if (!createChildSessionFn) + { + WLog_ERR(TAG, "unable to retrieve WinStationCreateChildSessionTransport function"); + goto out; + } + + HRESULT hStatus = createChildSessionFn(pipePath, 0x80); + if (!SUCCEEDED(hStatus)) + { + WLog_ERR(TAG, "error 0x%x when creating childSessionTransport", hStatus); + goto out; + } + + ConvertWCharNToUtf8(pipePath, 0x80, pipePathA, sizeof(pipePathA)); + WLog_DBG(TAG, "child session is at '%s'", pipePathA); + + HANDLE f = CreateFileW(pipePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + if (f == INVALID_HANDLE_VALUE) + { + WLog_ERR(TAG, "error when connecting to local named pipe"); + goto out; + } + + *pFile = f; + ret = TRUE; + +out: + FreeLibrary(hModule); + return ret; +} + +BIO* createChildSessionBio(void) +{ + HANDLE f = INVALID_HANDLE_VALUE; + if (!createChildSessionTransport(&f)) + return NULL; + + BIO* lowLevelBio = BIO_new(BIO_s_namedpipe()); + if (!lowLevelBio) + { + CloseHandle(f); + return NULL; + } + + BIO_set_handle(lowLevelBio, f); + BIO* bufferedBio = BIO_new(BIO_s_buffered_socket()); + + if (!bufferedBio) + { + BIO_free_all(lowLevelBio); + return FALSE; + } + + bufferedBio = BIO_push(bufferedBio, lowLevelBio); + + return bufferedBio; +} diff --git a/libfreerdp/core/childsession.h b/libfreerdp/core/childsession.h new file mode 100644 index 000000000..5adf8aef7 --- /dev/null +++ b/libfreerdp/core/childsession.h @@ -0,0 +1,27 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Connecting to windows child session + * + * Copyright 2023 David Fort + * + * 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. + */ + +#ifndef FREERDP_LIB_CORE_CHILDSESSION_H +#define FREERDP_LIB_CORE_CHILDSESSION_H + +#include + +FREERDP_LOCAL BIO* createChildSessionBio(void); + +#endif /* FREERDP_LIB_CORE_CHILDSESSION_H */ diff --git a/libfreerdp/core/connection.c b/libfreerdp/core/connection.c index 9861a15ce..508e6da2a 100644 --- a/libfreerdp/core/connection.c +++ b/libfreerdp/core/connection.c @@ -393,6 +393,7 @@ BOOL rdp_client_connect(rdpRdp* rdp) if (!status) return FALSE; + nego_set_childsession_enabled(rdp->nego, settings->ConnectChildSession); nego_set_send_preconnection_pdu(rdp->nego, settings->SendPreconnectionPdu); nego_set_preconnection_id(rdp->nego, settings->PreconnectionId); nego_set_preconnection_blob(rdp->nego, settings->PreconnectionBlob); @@ -421,7 +422,6 @@ BOOL rdp_client_connect(rdpRdp* rdp) if (!freerdp_settings_get_bool(settings, FreeRDP_TransportDumpReplay)) { - if (!rdp_client_transition_to_state(rdp, CONNECTION_STATE_NEGO)) return FALSE; diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 81d6702a0..2f1b2ed03 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -62,6 +62,7 @@ struct rdp_nego BOOL RestrictedAdminModeRequired; BOOL GatewayEnabled; BOOL GatewayBypassLocal; + BOOL ConnectChildSession; rdpTransport* transport; }; @@ -89,6 +90,7 @@ static const char* protocol_security_string(UINT32 security) return PROTOCOL_SECURITY_STRINGS[security]; } +static BOOL nego_tcp_connect(rdpNego* nego); static BOOL nego_transport_connect(rdpNego* nego); static BOOL nego_transport_disconnect(rdpNego* nego); static BOOL nego_security_connect(rdpNego* nego); @@ -99,31 +101,6 @@ static BOOL nego_process_negotiation_request(rdpNego* nego, wStream* s); static BOOL nego_process_negotiation_response(rdpNego* nego, wStream* s); static BOOL nego_process_negotiation_failure(rdpNego* nego, wStream* s); -static UINT32 nego_get_next_selected_protocol(rdpNego* nego) -{ - WINPR_ASSERT(nego); - - if (nego->EnabledProtocols[PROTOCOL_RDSAAD]) - return PROTOCOL_RDSAAD; - - if (nego->EnabledProtocols[PROTOCOL_RDSTLS]) - return PROTOCOL_RDSTLS; - - if (nego->EnabledProtocols[PROTOCOL_HYBRID_EX] || nego->EnabledProtocols[PROTOCOL_HYBRID]) - return PROTOCOL_HYBRID_EX; - - if (nego->EnabledProtocols[PROTOCOL_HYBRID]) - return PROTOCOL_HYBRID; - - if (nego->EnabledProtocols[PROTOCOL_SSL]) - return PROTOCOL_SSL; - - if (nego->EnabledProtocols[PROTOCOL_RDP]) - return PROTOCOL_RDP; - - WLog_ERR(TAG, "Invalid NEGO state 0x%08" PRIx32, nego_get_state(nego)); - return 0; -} /** * Negotiate protocol security and connect. * @@ -222,6 +199,12 @@ BOOL nego_connect(rdpNego* nego) return FALSE; } + if (!nego_tcp_connect(nego)) + { + WLog_ERR(TAG, "Failed to connect"); + return FALSE; + } + if (nego->SendPreconnectionPdu) { if (!nego_send_preconnection_pdu(nego)) @@ -391,6 +374,10 @@ static BOOL nego_tcp_connect(rdpNego* nego) TcpConnectTimeout); } } + else if (nego->ConnectChildSession) + { + nego->TcpConnected = transport_connect_childsession(nego->transport); + } else { nego->TcpConnected = @@ -1661,6 +1648,12 @@ void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdmin nego->RestrictedAdminModeRequired = RestrictedAdminModeRequired; } +void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled) +{ + WINPR_ASSERT(nego); + nego->ConnectChildSession = ChildSessionEnabled; +} + void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled) { nego->GatewayEnabled = GatewayEnabled; diff --git a/libfreerdp/core/nego.h b/libfreerdp/core/nego.h index ea1adad70..4bf1228d8 100644 --- a/libfreerdp/core/nego.h +++ b/libfreerdp/core/nego.h @@ -114,6 +114,7 @@ FREERDP_LOCAL BOOL nego_set_target(rdpNego* nego, const char* hostname, UINT16 p FREERDP_LOCAL void nego_set_negotiation_enabled(rdpNego* nego, BOOL NegotiateSecurityLayer); FREERDP_LOCAL void nego_set_restricted_admin_mode_required(rdpNego* nego, BOOL RestrictedAdminModeRequired); +FREERDP_LOCAL void nego_set_childsession_enabled(rdpNego* nego, BOOL ChildSessionEnabled); FREERDP_LOCAL void nego_set_gateway_enabled(rdpNego* nego, BOOL GatewayEnabled); FREERDP_LOCAL void nego_set_gateway_bypass_local(rdpNego* nego, BOOL GatewayBypassLocal); FREERDP_LOCAL void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp); diff --git a/libfreerdp/core/tcp.h b/libfreerdp/core/tcp.h index ddc1cf013..51f3e5420 100644 --- a/libfreerdp/core/tcp.h +++ b/libfreerdp/core/tcp.h @@ -41,6 +41,7 @@ #define BIO_TYPE_TSG 65 #define BIO_TYPE_SIMPLE 66 #define BIO_TYPE_BUFFERED 67 +#define BIO_TYPE_NAMEDPIPE 69 #define BIO_C_SET_SOCKET 1101 #define BIO_C_GET_SOCKET 1102 @@ -50,10 +51,12 @@ #define BIO_C_WRITE_BLOCKED 1106 #define BIO_C_WAIT_READ 1107 #define BIO_C_WAIT_WRITE 1108 +#define BIO_C_SET_HANDLE 1109 #define BIO_set_socket(b, s, c) BIO_ctrl(b, BIO_C_SET_SOCKET, c, s); #define BIO_get_socket(b, c) BIO_ctrl(b, BIO_C_GET_SOCKET, 0, (char*)c) #define BIO_get_event(b, c) BIO_ctrl(b, BIO_C_GET_EVENT, 0, (char*)c) +#define BIO_set_handle(b, h) BIO_ctrl(b, BIO_C_SET_HANDLE, 0, h) #define BIO_set_nonblock(b, c) BIO_ctrl(b, BIO_C_SET_NONBLOCK, c, NULL) #define BIO_read_blocked(b) BIO_ctrl(b, BIO_C_READ_BLOCKED, 0, NULL) #define BIO_write_blocked(b) BIO_ctrl(b, BIO_C_WRITE_BLOCKED, 0, NULL) diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index 6f46a080c..7046c87a5 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -25,6 +25,7 @@ static const size_t bool_list_indices[] = { FreeRDP_BitmapCompressionDisabled, FreeRDP_CertificateCallbackPreferPEM, FreeRDP_CompressionEnabled, + FreeRDP_ConnectChildSession, FreeRDP_ConsoleSession, FreeRDP_CredentialsFromStdin, FreeRDP_DeactivateClientDecoding, diff --git a/libfreerdp/core/transport.c b/libfreerdp/core/transport.c index 763286802..7d8bd554e 100644 --- a/libfreerdp/core/transport.c +++ b/libfreerdp/core/transport.c @@ -53,6 +53,7 @@ #include "proxy.h" #include "utils.h" #include "state.h" +#include "childsession.h" #include "gateway/rdg.h" #include "gateway/wst.h" @@ -578,6 +579,18 @@ BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 por return status; } +BOOL transport_connect_childsession(rdpTransport* transport) +{ + WINPR_ASSERT(transport); + + transport->frontBio = createChildSessionBio(); + if (!transport->frontBio) + return FALSE; + + transport->layer = TRANSPORT_LAYER_TSG; + return TRUE; +} + BOOL transport_accept_rdp(rdpTransport* transport) { if (!transport) diff --git a/libfreerdp/core/transport.h b/libfreerdp/core/transport.h index 0e5b82646..aff067bcd 100644 --- a/libfreerdp/core/transport.h +++ b/libfreerdp/core/transport.h @@ -24,6 +24,7 @@ typedef enum { TRANSPORT_LAYER_TCP, TRANSPORT_LAYER_TLS, + TRANSPORT_LAYER_NAMEDPIPE, TRANSPORT_LAYER_TSG, TRANSPORT_LAYER_TSG_TLS, TRANSPORT_LAYER_CLOSED @@ -56,6 +57,7 @@ typedef state_run_t (*TransportRecv)(rdpTransport* transport, wStream* stream, v FREERDP_LOCAL wStream* transport_send_stream_init(rdpTransport* transport, size_t size); FREERDP_LOCAL BOOL transport_connect(rdpTransport* transport, const char* hostname, UINT16 port, DWORD timeout); +FREERDP_LOCAL BOOL transport_connect_childsession(rdpTransport* transport); FREERDP_LOCAL BOOL transport_attach(rdpTransport* transport, int sockfd); FREERDP_LOCAL BOOL transport_disconnect(rdpTransport* transport); FREERDP_LOCAL BOOL transport_connect_rdp(rdpTransport* transport); diff --git a/libfreerdp/core/utils.c b/libfreerdp/core/utils.c index 17d3352f2..15547c189 100644 --- a/libfreerdp/core/utils.c +++ b/libfreerdp/core/utils.c @@ -148,6 +148,9 @@ auth_status utils_authenticate(freerdp* instance, rdp_auth_reason reason, BOOL o if (freerdp_shall_disconnect_context(instance->context)) return AUTH_FAILED; + if (settings->ConnectChildSession) + return AUTH_NO_CREDENTIALS; + /* Ask for auth data if no or an empty username was specified or no password was given */ if (utils_str_is_empty(freerdp_settings_get_string(settings, FreeRDP_Username)) || (settings->Password == NULL && settings->RedirectionPassword == NULL))