From d09ebb2d10fbd9869cbfccf186e874c24818a9c6 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 30 Jun 2025 14:59:52 +0100 Subject: [PATCH 01/10] Add initial CCP to libipm --- libipm/Makefile.am | 4 + libipm/ccp.c | 122 +++++++++++++++++++++++++++++ libipm/ccp.h | 136 +++++++++++++++++++++++++++++++++ libipm/ccp_application_types.c | 58 ++++++++++++++ libipm/ccp_application_types.h | 52 +++++++++++++ libipm/libipm_facilities.h | 1 + 6 files changed, 373 insertions(+) create mode 100644 libipm/ccp.c create mode 100644 libipm/ccp.h create mode 100644 libipm/ccp_application_types.c create mode 100644 libipm/ccp_application_types.h diff --git a/libipm/Makefile.am b/libipm/Makefile.am index 8da084bc..6ec0c810 100644 --- a/libipm/Makefile.am +++ b/libipm/Makefile.am @@ -13,6 +13,10 @@ libipm_la_SOURCES = \ libipm_recv.c \ libipm_facilities.h \ libipm_private.h \ + ccp.h \ + ccp.c \ + ccp_application_types.h \ + ccp_application_types.c \ eicp.h \ eicp.c \ ercp.h \ diff --git a/libipm/ccp.c b/libipm/ccp.c new file mode 100644 index 00000000..3df33721 --- /dev/null +++ b/libipm/ccp.c @@ -0,0 +1,122 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors + * + * 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. + */ + +/** + * + * @file libipm/ccp.c + * @brief CCP definitions + * @author Matt Burt + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include +#include + +#include "ccp.h" +#include "libipm.h" +#include "trans.h" + +/*****************************************************************************/ +static const char * +msgno_to_str(unsigned short n) +{ + return + (n == E_CCP_CLOSE_CONNECTION_REQUEST) ? "E_CCP_CLOSE_CONNECTION_REQUEST" : + NULL; +} + +/*****************************************************************************/ +const char * +ccp_msgno_to_str(enum ccp_msg_code n, char *buff, unsigned int buff_size) +{ + const char *str = msgno_to_str((unsigned short)n); + + if (str == NULL) + { + (void)snprintf(buff, buff_size, "[code #%d]", (int)n); + } + else + { + (void)snprintf(buff, buff_size, "%s", str); + } + + return buff; +} + +/*****************************************************************************/ +void +ccp_trans_from_scp_trans(struct trans *trans, + ttrans_data_in callback_func, + void *callback_data) +{ + libipm_change_facility(trans, LIBIPM_FAC_SCP, LIBIPM_FAC_CCP); + trans->trans_data_in = callback_func; + trans->callback_data = callback_data; +} + +/*****************************************************************************/ +int +ccp_msg_in_check_available(struct trans *trans, int *available) +{ + return libipm_msg_in_check_available(trans, available); +} + +/*****************************************************************************/ +enum ccp_msg_code +ccp_msg_in_get_msgno(const struct trans *trans) +{ + return (enum ccp_msg_code)libipm_msg_in_get_msgno(trans); +} + +/*****************************************************************************/ +void +ccp_msg_in_reset(struct trans *trans) +{ + libipm_msg_in_reset(trans); +} + +/*****************************************************************************/ +int +ccp_send_close_connection_request(struct trans *trans, + enum ccp_close_reason_type reason) +{ + return libipm_msg_out_simple_send( + trans, + (int)E_CCP_CLOSE_CONNECTION_REQUEST, + "i", reason); +} + +/*****************************************************************************/ +int +ccp_get_close_connection_request(struct trans *trans, + enum ccp_close_reason_type *reason) +{ + /* Intermediate values */ + int32_t i_reason; + + int rv = libipm_msg_in_parse( trans, "i", &i_reason); + if (rv == 0) + { + *reason = (enum ccp_close_reason_type)i_reason; + } + + return rv; +} diff --git a/libipm/ccp.h b/libipm/ccp.h new file mode 100644 index 00000000..419f0b40 --- /dev/null +++ b/libipm/ccp.h @@ -0,0 +1,136 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2025, all xrdp contributors + * + * 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. + */ + +/** + * + * @file libipm/ccp.h + * @brief CCP declarations + * @author Matt Burt + * + * Functions in this file use the following naming conventions:- + * + * E_CCP_{msg}_REQUEST is sent by ccp_send_{msg}_request() + * E_CCP_{msg}_REQUEST is parsed by ccp_get_{msg}_request() + * E_CCP_{msg}_RESPONSE is sent by ccp_send_{msg}_response() + * E_CCP_{msg}_RESPONSE is parsed by ccp_get_{msg}_response() + * E_CCP_{msg}_EVENT is sent by ccp_send_{msg}_event() + * E_CCP_{msg}_EVENT is parsed by ccp_get_{msg}_event() + */ + +#ifndef CCP_H +#define CCP_H + +#include "arch.h" +#include "trans.h" +#include "ccp_application_types.h" + +/* Message codes */ +enum ccp_msg_code +{ + E_CCP_CLOSE_CONNECTION_REQUEST // sesexec -> xrdp + // No E_CCP_CLOSE_CONNECTION_RESPONSE +}; + +/* Common facilities */ + +/** + * Convert a message code to a string for output + * @param n Message code + * @param buff to contain string + * @param buff_size length of buff + * @return buff is returned for convenience. + */ +const char * +ccp_msgno_to_str(enum ccp_msg_code n, char *buff, unsigned int buff_size); + +/* Connection management facilities */ + +/** + * Converts an SCP transport to an CCP transport. + * + * This is done following successful transmission or receipt of an + * E_SCP_CONNECT_SESSION_RESPONSE + * + * @param trans connected endpoint + * @param callback_func New callback function for CCP messages. + * @param callback_data New argument for callback function + */ +void +ccp_trans_from_scp_trans(struct trans *trans, + ttrans_data_in callback_func, + void *callback_data); + +/** + * Checks an CCP transport to see if a complete message is + * available for parsing + * + * @param trans CCP transport + * @param[out] available != 0 if a complete message is available + * @return != 0 for error + */ +int +ccp_msg_in_check_available(struct trans *trans, int *available); + +/** + * Gets the CCP message number of an incoming message + * + * @param trans CCP transport + * @return message in the buffer + * + * The results of calling this routine before ccp_msg_in_check_available() + * states a message is available are undefined. + */ +enum ccp_msg_code +ccp_msg_in_get_msgno(const struct trans *trans); + +/** + * Resets an CCP message buffer ready to receive the next message + * + * @param trans libipm transport + */ +void +ccp_msg_in_reset(struct trans *trans); + +/* -------------------- Session messages-------------------- */ +/** + * Send an E_CCP_CLOSE_CONNECTION_REQUEST + * + * Direction : sesexec -> xrdp + * + * @param trans CCP transport + * @param reason reason_code + * @return != 0 for error + */ +int +ccp_send_close_connection_request(struct trans *trans, + enum ccp_close_reason_type reason); + +/** + * Parse an incoming E_CCP_CLOSE_CONNECTION_REQUEST + * + * Direction : sesexec -> xrdp + * + * @param trans CCP transport + * @param[out] reason reason_code + * @return != 0 for error + */ +int +ccp_get_close_connection_request(struct trans *trans, + enum ccp_close_reason_type *reason); + +#endif /* CCP_H */ diff --git a/libipm/ccp_application_types.c b/libipm/ccp_application_types.c new file mode 100644 index 00000000..b2518e44 --- /dev/null +++ b/libipm/ccp_application_types.c @@ -0,0 +1,58 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors + * + * 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. + */ + +/** + * + * @file libipm/ccp_application_types.c + * @brief Support routines for types in ccp_application_types.h + * @author Matt Burt + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include "ccp_application_types.h" + +/*****************************************************************************/ +const char * +ccp_close_reason_to_str(enum ccp_close_reason_type n, + char *buff, unsigned int buff_size) +{ + const char *str = + (n == CCP_CLOSE_RPC_INITIATED_DISCONNECT) + ? "Connection closed by administrator request" : + (n == CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION) + ? "Another connection was made to the session" : + (n == CCP_CLOSE_LOGOFF_BY_USER) + ? "The user logged out of the session" : + /* Default */ NULL; + + if (str == NULL) + { + (void)snprintf(buff, buff_size, "[ccp reason code #%d]", (int)n); + } + else + { + (void)snprintf(buff, buff_size, "%s", str); + } + + return buff; +} diff --git a/libipm/ccp_application_types.h b/libipm/ccp_application_types.h new file mode 100644 index 00000000..55fe6c78 --- /dev/null +++ b/libipm/ccp_application_types.h @@ -0,0 +1,52 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2022, all xrdp contributors + * + * 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. + */ + +/** + * + * @file libipm/ccp_application_types.h + * @brief ccp type declarations intended for use in the application + * @author Simone Fedele/ Matt Burt + */ + +#ifndef CCP_APPLICATION_TYPES_H +#define CCP_APPLICATION_TYPES_H + +#include + +/** + * Select the reason for a close connection request + */ +enum ccp_close_reason_type +{ + CCP_CLOSE_RPC_INITIATED_DISCONNECT = 1, + CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION, + CCP_CLOSE_LOGOFF_BY_USER +}; + +/** + * Convert an ccp_close_reason_type to a readable string for output + * @param n Message code + * @param buff to contain string + * @param buff_size length of buff + * @return buff is returned for convenience. + */ +const char * +ccp_close_reason_to_str(enum ccp_close_reason_type n, + char *buff, unsigned int buff_size); + +#endif /* CCP_APPLICATION_TYPES_H */ diff --git a/libipm/libipm_facilities.h b/libipm/libipm_facilities.h index 16b1cd2c..4dbd2561 100644 --- a/libipm/libipm_facilities.h +++ b/libipm/libipm_facilities.h @@ -30,6 +30,7 @@ enum libipm_facility LIBIPM_FAC_SCP = 1, /**< SCP - Sesman Control Protocol */ LIBIPM_FAC_EICP, /**< EICP - Executive Initialization Control Protocol */ LIBIPM_FAC_ERCP, /**< ERCP - Executive Run-time Control Protocol */ + LIBIPM_FAC_CCP, /**< CCP - Connection Control Protocol */ LIBIPM_FAC_TEST = 65535 /**< Used for unit testing */ }; From 7433ded30d9829d64d1f5ba06b137cb6db6282a3 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 4 Jul 2025 12:00:28 +0100 Subject: [PATCH 02/10] Use correct symbolic names for TS_SET_ERROR_INFO_PDU --- common/ms-rdpbcgr.h | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/common/ms-rdpbcgr.h b/common/ms-rdpbcgr.h index c92ad625..53f59065 100644 --- a/common/ms-rdpbcgr.h +++ b/common/ms-rdpbcgr.h @@ -272,27 +272,15 @@ /* RDP5 disconnect PDU */ /* Set Error Info PDU Data: errorInfo (2.2.5.1.1) */ -/* TODO: to be renamed */ -#define exDiscReasonNoInfo 0x0000 -#define exDiscReasonAPIInitiatedDisconnect 0x0001 -#define exDiscReasonAPIInitiatedLogoff 0x0002 -#define exDiscReasonServerIdleTimeout 0x0003 -#define exDiscReasonServerLogonTimeout 0x0004 -#define exDiscReasonReplacedByOtherConnection 0x0005 -#define exDiscReasonOutOfMemory 0x0006 -#define exDiscReasonServerDeniedConnection 0x0007 -#define exDiscReasonServerDeniedConnectionFips 0x0008 -#define exDiscReasonLicenseInternal 0x0100 -#define exDiscReasonLicenseNoLicenseServer 0x0101 -#define exDiscReasonLicenseNoLicense 0x0102 -#define exDiscReasonLicenseErrClientMsg 0x0103 -#define exDiscReasonLicenseHwidDoesntMatchLicense 0x0104 -#define exDiscReasonLicenseErrClientLicense 0x0105 -#define exDiscReasonLicenseCantFinishProtocol 0x0106 -#define exDiscReasonLicenseClientEndedProtocol 0x0107 -#define exDiscReasonLicenseErrClientEncryption 0x0108 -#define exDiscReasonLicenseCantUpgradeLicense 0x0109 -#define exDiscReasonLicenseNoRemoteConnections 0x010a +#define ERRINFO_NONE 0x0000 +#define ERRINFO_RPC_INITIATED_DISCONNECT 0x0001 +#define ERRINFO_RPC_INITIATED_LOGOFF 0x0002 +#define ERRINFO_IDLE_TIMEOUT 0x0003 +#define ERRINFO_LOGON_TIMEOUT 0x0004 +#define ERRINFO_DISCONNECTED_BY_OTHERCONNECTION 0x0005 +#define ERRINFO_OUT_OF_MEMORY 0x0006 +#define ERRINFO_SERVER_DENIED_CONNECTION 0x0007 +#define ERRINFO_LOGOFF_BY_USER 0x000c /* Virtual channel PDU (2.2.6.1) */ #define CHANNEL_CHUNK_LENGTH 1600 From d015535065c76f1604bb8e875c971e5b56cc2a25 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 8 Jul 2025 12:32:34 +0100 Subject: [PATCH 03/10] Add CCP support to xrdp This allows sesexec to send a reason for a connection close request to xrdp. xrdp is also updated to support server initiated disconnection sequences from [MS-RDPBCGR] 1.3.1.4, along with reporting a reason to the client for the disconnection. --- common/ms-rdpbcgr.h | 4 ++ libipm/ccp_application_types.c | 2 + libipm/ccp_application_types.h | 3 +- libxrdp/libxrdp.c | 41 +++++++++++- libxrdp/libxrdp.h | 12 ++++ libxrdp/libxrdpinc.h | 12 +++- libxrdp/xrdp_rdp.c | 9 +-- tests/xrdp/Makefile.am | 1 + xrdp/Makefile.am | 1 + xrdp/xrdp.h | 32 ++++++++++ xrdp/xrdp_login_wnd.c | 2 + xrdp/xrdp_mm.c | 113 ++++++++++++++++++++++----------- xrdp/xrdp_mm_ccp.c | 107 +++++++++++++++++++++++++++++++ xrdp/xrdp_process.c | 4 +- xrdp/xrdp_types.h | 3 + 15 files changed, 296 insertions(+), 50 deletions(-) create mode 100644 xrdp/xrdp_mm_ccp.c diff --git a/common/ms-rdpbcgr.h b/common/ms-rdpbcgr.h index 53f59065..52d32f64 100644 --- a/common/ms-rdpbcgr.h +++ b/common/ms-rdpbcgr.h @@ -82,6 +82,7 @@ #define RNS_UD_32BPP_SUPPORT 0x0008 /* Client Core Data: earlyCapabilityFlags (2.2.1.3.2) */ +#define RNS_UD_CS_SUPPORT_ERRINFO_PDU 0x0001 #define RNS_UD_CS_WANT_32BPP_SESSION 0x0002 #define RNS_UD_CS_SUPPORT_MONITOR_LAYOUT_PDU 0x0040 #define RNS_UD_CS_SUPPORT_DYNVC_GFX_PROTOCOL 0x0100 @@ -280,7 +281,10 @@ #define ERRINFO_DISCONNECTED_BY_OTHERCONNECTION 0x0005 #define ERRINFO_OUT_OF_MEMORY 0x0006 #define ERRINFO_SERVER_DENIED_CONNECTION 0x0007 +#define ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES 0x0009 #define ERRINFO_LOGOFF_BY_USER 0x000c +#define ERRINFO_SERVER_DWM_CRASH 0x0010 +#define ERRINFO_SERVER_CSRSS_CRASH 0x0018 /* Virtual channel PDU (2.2.6.1) */ #define CHANNEL_CHUNK_LENGTH 1600 diff --git a/libipm/ccp_application_types.c b/libipm/ccp_application_types.c index b2518e44..b14f0977 100644 --- a/libipm/ccp_application_types.c +++ b/libipm/ccp_application_types.c @@ -43,6 +43,8 @@ ccp_close_reason_to_str(enum ccp_close_reason_type n, ? "Another connection was made to the session" : (n == CCP_CLOSE_LOGOFF_BY_USER) ? "The user logged out of the session" : + (n == CCP_CLOSE_SOFTWARE_FAILURE) + ? "A software failure has occurred" : /* Default */ NULL; if (str == NULL) diff --git a/libipm/ccp_application_types.h b/libipm/ccp_application_types.h index 55fe6c78..8e700d1f 100644 --- a/libipm/ccp_application_types.h +++ b/libipm/ccp_application_types.h @@ -35,7 +35,8 @@ enum ccp_close_reason_type { CCP_CLOSE_RPC_INITIATED_DISCONNECT = 1, CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION, - CCP_CLOSE_LOGOFF_BY_USER + CCP_CLOSE_LOGOFF_BY_USER, + CCP_CLOSE_SOFTWARE_FAILURE }; /** diff --git a/libxrdp/libxrdp.c b/libxrdp/libxrdp.c index 64ce2c12..fda20fca 100644 --- a/libxrdp/libxrdp.c +++ b/libxrdp/libxrdp.c @@ -73,9 +73,46 @@ libxrdp_exit(struct xrdp_session *session) /******************************************************************************/ int EXPORT_CC -libxrdp_disconnect(struct xrdp_session *session) +libxrdp_disconnect(struct xrdp_session *session, int errinfo) { - return xrdp_rdp_disconnect((struct xrdp_rdp *)session->rdp); + int rv = 0; + struct trans *trans = NULL; + struct xrdp_rdp *rdp = NULL; + int early_capability_flags = 0; + + if (session != NULL) + { + trans = session->trans; + rdp = (struct xrdp_rdp *)session->rdp; + if (session->client_info != NULL) + { + early_capability_flags = + session->client_info->mcs_early_capability_flags; + } + } + + if (trans != NULL && trans->status == TRANS_STATUS_UP && + trans->sck >= 0 && rdp != NULL) + { + /* Only send the error info PDU if the client has + * indicated it can receive it */ + if ((early_capability_flags & RNS_UD_CS_SUPPORT_ERRINFO_PDU) != 0) + { + rv = xrdp_rdp_send_set_error(rdp, errinfo); + } + + if (rv == 0) + { + rv = xrdp_rdp_send_deactivate(rdp); + } + + if (rv == 0) + { + rv = xrdp_rdp_disconnect(rdp); + } + } + + return rv; } /******************************************************************************/ diff --git a/libxrdp/libxrdp.h b/libxrdp/libxrdp.h index b8b60136..1bac72ef 100644 --- a/libxrdp/libxrdp.h +++ b/libxrdp/libxrdp.h @@ -456,6 +456,18 @@ xrdp_rdp_send_deactivate(struct xrdp_rdp *self); int xrdp_rdp_send_session_info(struct xrdp_rdp *self, const char *data, int data_bytes); +/** + * Send a [MS-RDPBCGR] TS_SET_ERROR_INFO_PDU message + * @param self xrdp_rdp struct + * @param reason errinfo code ([MS-RDPBCGR] 2.2.5.1.1) + * @return != 0 for error + + * The caller is responsible for checking the client supports + * reception of this message (see 2.2.5.1) + */ +int +xrdp_rdp_send_set_error(struct xrdp_rdp *self, int reason); + /** * Request output suppress or resume * diff --git a/libxrdp/libxrdpinc.h b/libxrdp/libxrdpinc.h index b2d86adb..d5ca24eb 100644 --- a/libxrdp/libxrdpinc.h +++ b/libxrdp/libxrdpinc.h @@ -104,8 +104,18 @@ struct xrdp_session * libxrdp_init(tbus id, struct trans *trans, const char *xrdp_ini); int libxrdp_exit(struct xrdp_session *session); +/** + * Sends a disconnect sequence from the server ([MS-RDPBCGR] 1.3.1.4.x) + * @param session xrdp session + * @param errinfo Reason ([MS-RDPBCGR] 2.2.5.1.1) + * @return != 0 for an error + * + * After this call, the rdp channel is closed and cannot be re-used. This + * routine can be called more than once, but only the first call is + * effective. + */ int -libxrdp_disconnect(struct xrdp_session *session); +libxrdp_disconnect(struct xrdp_session *session, int errinfo); int libxrdp_process_incoming(struct xrdp_session *session); int EXPORT_CC diff --git a/libxrdp/xrdp_rdp.c b/libxrdp/xrdp_rdp.c index 97fe7ffd..f2397cf7 100644 --- a/libxrdp/xrdp_rdp.c +++ b/libxrdp/xrdp_rdp.c @@ -1364,11 +1364,9 @@ xrdp_rdp_send_disconnect_query_response(struct xrdp_rdp *self) return 0; } -#if 0 /* not used */ /*****************************************************************************/ -/* Send a [MS-RDPBCGR] TS_SET_ERROR_INFO_PDU message */ -static int -xrdp_rdp_send_disconnect_reason(struct xrdp_rdp *self, int reason) +int +xrdp_rdp_send_set_error(struct xrdp_rdp *self, int reason) { struct stream *s; @@ -1378,7 +1376,7 @@ xrdp_rdp_send_disconnect_reason(struct xrdp_rdp *self, int reason) if (xrdp_rdp_init_data(self, s) != 0) { LOG(LOG_LEVEL_ERROR, - "xrdp_rdp_send_disconnect_reason: xrdp_rdp_init_data failed"); + "xrdp_rdp_send_set_error_pdu: xrdp_rdp_init_data failed"); free_stream(s); return 1; } @@ -1399,7 +1397,6 @@ xrdp_rdp_send_disconnect_reason(struct xrdp_rdp *self, int reason) free_stream(s); return 0; } -#endif /*****************************************************************************/ /* Process a [MS-RDPRFX] TS_FRAME_ACKNOWLEDGE_PDU message */ diff --git a/tests/xrdp/Makefile.am b/tests/xrdp/Makefile.am index 505e4887..79e4d3bb 100644 --- a/tests/xrdp/Makefile.am +++ b/tests/xrdp/Makefile.am @@ -61,6 +61,7 @@ test_xrdp_LDADD = \ $(top_builddir)/libxrdp/libxrdp.la \ $(top_builddir)/xrdp/lang.o \ $(top_builddir)/xrdp/xrdp_mm.o \ + $(top_builddir)/xrdp/xrdp_mm_ccp.o \ $(top_builddir)/xrdp/xrdp_wm.o \ $(top_builddir)/xrdp/xrdp_font.o \ $(top_builddir)/xrdp/xrdp_egfx.o \ diff --git a/xrdp/Makefile.am b/xrdp/Makefile.am index 996eae92..685635dc 100644 --- a/xrdp/Makefile.am +++ b/xrdp/Makefile.am @@ -71,6 +71,7 @@ xrdp_SOURCES = \ xrdp_listen.c \ xrdp_login_wnd.c \ xrdp_mm.c \ + xrdp_mm_ccp.c \ xrdp_mm.h \ xrdp_painter.c \ xrdp_process.c \ diff --git a/xrdp/xrdp.h b/xrdp/xrdp.h index 15f9770e..e4d4e88d 100644 --- a/xrdp/xrdp.h +++ b/xrdp/xrdp.h @@ -503,6 +503,31 @@ xrdp_mm_suppress_output(struct xrdp_mm *self, int suppress, int left, int top, int right, int bottom); int xrdp_mm_up_and_running(struct xrdp_mm *self); + +/** + * Ask the xrdp process (or thread) to terminate + * @param self xrdp_mm struct + * @param errinfo Error code to return to the client + * + * Execution continues after this call until the main process loop + * is reached. This routine could be called multiple times. In this + * instance the first code set is passed back to the client. + */ +void +xrdp_mm_set_fatal(struct xrdp_mm *self, int errinfo); + +/** + * Tell the user via the log window a fatal error has occurred + * @param self xrdp_mm struct + * @param errinfo Error code to return to the client + * + * The log window must already contain the fatal error. + * When the user presses OK in the log window, the xrdp process + * (or thread) is terminated. + */ +void +xrdp_mm_logwnd_fatal(struct xrdp_mm *self, int errinfo); + int xrdp_mm_send_unicode_to_chansrv(struct xrdp_mm *self, int key_down, @@ -535,4 +560,11 @@ xrdp_mm_egfx_send_planar_bitmap(struct xrdp_mm *self, struct xrdp_bitmap *bitmap, struct xrdp_rect *rect, int surface_id, int x, int y); + +/* xrdp_mm_cpp.c */ + +/* Callback registered for sesman communication replies over CCP */ +int +xrdp_mm_ccp_data_in(struct trans *trans); + #endif diff --git a/xrdp/xrdp_login_wnd.c b/xrdp/xrdp_login_wnd.c index 80d0a737..da1535e7 100644 --- a/xrdp/xrdp_login_wnd.c +++ b/xrdp/xrdp_login_wnd.c @@ -216,6 +216,8 @@ xrdp_wm_cancel_clicked(struct xrdp_bitmap *wnd) { if (wnd->wm->pro_layer != 0) { + wnd->wm->pro_layer->errinfo = + ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES; g_set_wait_obj(wnd->wm->pro_layer->self_term_event); } } diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 2feb6060..536682d3 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -28,6 +28,7 @@ #include "guid.h" #include "ms-rdpedisp.h" #include "ms-rdpbcgr.h" +#include "ccp.h" #include "scp.h" #include #include "xrdp_encoder.h" @@ -1991,6 +1992,26 @@ xrdp_mm_up_and_running(struct xrdp_mm *self) return 0; } +/******************************************************************************/ +void +xrdp_mm_set_fatal(struct xrdp_mm *self, int errinfo) +{ + if (self->wm->pro_layer->errinfo == ERRINFO_NONE) + { + self->wm->pro_layer->errinfo = errinfo; + } + g_set_wait_obj(self->wm->pro_layer->self_term_event); +} + +/******************************************************************************/ +void +xrdp_mm_logwnd_fatal(struct xrdp_mm *self, int errinfo) +{ + xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "Close the log window to exit."); + self->wm->pro_layer->errinfo = errinfo; + self->wm->fatal_error_in_log_window = 1; +} + /*****************************************************************************/ /* open response from client going to channel server */ static int @@ -2621,9 +2642,9 @@ xrdp_mm_process_login_response(struct xrdp_mm *self) if (self->wm->client_info->require_credentials) { /* Credentials had to be specified, but were invalid */ - g_set_wait_obj(self->wm->pro_layer->self_term_event); LOG(LOG_LEVEL_ERROR, "require_credentials is set, " "but the user could not be logged in"); + xrdp_mm_set_fatal(self, ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES); } if (server_closed) @@ -2633,9 +2654,8 @@ xrdp_mm_process_login_response(struct xrdp_mm *self) xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", "Login retry limit reached"); } - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", - "Close the log window to exit."); - self->wm->fatal_error_in_log_window = 1; + xrdp_mm_logwnd_fatal(self, + ERRINFO_SERVER_INSUFFICIENT_PRIVILEGES); /* Transport can be deleted now */ self->delete_sesman_trans = 1; } @@ -2717,16 +2737,24 @@ xrdp_mm_process_connect_session_response(struct xrdp_mm *self) rv = scp_get_connect_session_response(self->sesman_trans, &status, &self->sesman_display_fd, &self->sesman_chansrv_fd); - /* Following this response, the sesman trans is closed */ - self->delete_sesman_trans = 1; - - if (rv == 0) + if (rv != 0) + { + self->delete_sesman_trans = 1; + } + else { if (status == E_SCP_SCONNECT_OK) { xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "Got connection details for session"); + /* Convert the sesman transport fron an SCP transport + * to a CCP transport */ + ccp_trans_from_scp_trans(self->sesman_trans, + xrdp_mm_ccp_data_in, + self); + self->sesman_trans_is_ccp = 1; + /* Carry on with the connect state machine */ xrdp_mm_connect_sm(self); } @@ -2745,10 +2773,11 @@ xrdp_mm_process_connect_session_response(struct xrdp_mm *self) xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "Can't create session for user %s - %s", username, buff); - xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", - "Close the log window to exit."); - self->wm->fatal_error_in_log_window = 1; + xrdp_mm_logwnd_fatal(self, + ERRINFO_SERVER_DWM_CRASH); xrdp_wm_mod_connect_done(self->wm, 1); + // The sesman tranport is now useless to us. + self->delete_sesman_trans = 1; } } @@ -2756,7 +2785,7 @@ xrdp_mm_process_connect_session_response(struct xrdp_mm *self) } /*****************************************************************************/ -/* This is the callback registered for sesman communication replies. */ +/* This is the callback registered for sesman communication replies over SCP */ static int xrdp_mm_scp_data_in(struct trans *trans) { @@ -3389,19 +3418,15 @@ xrdp_mm_connect_sm(struct xrdp_mm *self) if (!self->mmcs_expecting_msg) { - /* We don't need the sesman transport anymore */ - if (self->sesman_trans != NULL) - { - self->delete_sesman_trans = 1; - } - /* Close any uncomsumed file descriptors from sesman */ close_sesman_file_descriptors(self); xrdp_wm_mod_connect_done(self->wm, status); + /* Make sure the module is cleaned up if we weren't successful */ if (status != 0) { + self->delete_sesman_trans = 1; xrdp_mm_module_cleanup(self); } } @@ -3776,35 +3801,44 @@ xrdp_mm_draw_dirty(struct xrdp_mm *self) int xrdp_mm_check_wait_objs(struct xrdp_mm *self) { - int rv; + int rv = 0; if (self == 0) { return 0; } - rv = 0; - if (self->sesman_trans != NULL && !self->delete_sesman_trans && self->sesman_trans->status == TRANS_STATUS_UP) { if (trans_check_wait_objs(self->sesman_trans) != 0) { - if (self->mmcs_expecting_msg) + if (self->sesman_trans_is_ccp) { - /* The sesman transport has failed with an - * outstanding message */ - xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, - "Unexpected sesman failure - check sesman log"); - xrdp_wm_mod_connect_done(self->wm, 1); + /* Comms with the sesexec has failed - we have to assume + * the session has failed */ + xrdp_mm_set_fatal(self, ERRINFO_LOGOFF_BY_USER); + } + else + { + /* We're still using the SCP transport for + * pre-connection activities */ + if (self->mmcs_expecting_msg) + { + /* The sesman transport has failed with an + * outstanding message while connecting */ + xrdp_wm_log_msg(self->wm, LOG_LEVEL_ERROR, "Unexpected" + " sesman failure - check sesman log"); + xrdp_wm_mod_connect_done(self->wm, 1); + } + if (self->wm->hide_log_window) + { + /* if hide_log_window, this is fatal */ + rv = 1; + } } self->delete_sesman_trans = 1; - if (self->wm->hide_log_window) - { - /* if hide_log_window, this is fatal */ - rv = 1; - } } } if (self->delete_sesman_trans) @@ -3827,9 +3861,12 @@ xrdp_mm_check_wait_objs(struct xrdp_mm *self) if (self->mod != NULL) { - if (self->mod->mod_check_wait_objs != NULL) + if (self->mod->mod_check_wait_objs != NULL && + self->mod->mod_check_wait_objs(self->mod) != 0) { - rv = self->mod->mod_check_wait_objs(self->mod); + /* Comms with the module has failed - we have to assume + * the display server, and hence the session has failed */ + xrdp_mm_set_fatal(self, ERRINFO_LOGOFF_BY_USER); } } @@ -3859,7 +3896,7 @@ xrdp_mm_check_wait_objs(struct xrdp_mm *self) { if (self->egfx_up) { - rv = xrdp_mm_draw_dirty(self); + rv |= xrdp_mm_draw_dirty(self); xrdp_region_delete(self->wm->screen_dirty_region); self->wm->screen_dirty_region = NULL; self->wm->last_screen_draw_time = now; @@ -5259,7 +5296,7 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self) self->wm->screen->height, self->wm->screen->bpp) != 0) { - g_set_wait_obj(self->wm->pro_layer->self_term_event); /* kill session */ + xrdp_mm_set_fatal(self, ERRINFO_SERVER_DWM_CRASH); } } @@ -5269,7 +5306,7 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self) { LOG(LOG_LEVEL_ERROR, "Unexpected display value %d setting up module", self->display); - g_set_wait_obj(self->wm->pro_layer->self_term_event); /* kill session */ + xrdp_mm_set_fatal(self, ERRINFO_SERVER_DWM_CRASH); } else if (self->code == XVNC_SESSION_CODE) { @@ -5285,7 +5322,7 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self) { LOG(LOG_LEVEL_ERROR, "Unexpected session code %d setting up module", self->code); - g_set_wait_obj(self->wm->pro_layer->self_term_event); /* kill session */ + xrdp_mm_set_fatal(self, ERRINFO_SERVER_DWM_CRASH); } } diff --git a/xrdp/xrdp_mm_ccp.c b/xrdp/xrdp_mm_ccp.c new file mode 100644 index 00000000..8efa630c --- /dev/null +++ b/xrdp/xrdp_mm_ccp.c @@ -0,0 +1,107 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2014 + * + * 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. + * + * module manager + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif +#include "ccp.h" +#include "xrdp.h" +#include "log.h" + +/*****************************************************************************/ +/** + * We've been asked by sesman/sesexec to close the connection + */ +static int +xrdp_mm_process_close_ccp_connection_request(struct xrdp_mm *self) +{ + enum ccp_close_reason_type reason; + + int rv = ccp_get_close_connection_request(self->sesman_trans, &reason); + if (rv == 0) + { + int errinfo; + char buff[64]; + + switch (reason) + { + case CCP_CLOSE_RPC_INITIATED_DISCONNECT: + errinfo = ERRINFO_RPC_INITIATED_DISCONNECT; + break; + + case CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION: + errinfo = ERRINFO_DISCONNECTED_BY_OTHERCONNECTION; + break; + + case CCP_CLOSE_LOGOFF_BY_USER: + errinfo = ERRINFO_LOGOFF_BY_USER; + break; + + case CCP_CLOSE_SOFTWARE_FAILURE: + errinfo = ERRINFO_SERVER_CSRSS_CRASH; + break; + + default: + LOG(LOG_LEVEL_WARNING, "Unexpected close connection reason %d", + (int)reason); + errinfo = ERRINFO_LOGOFF_BY_USER; + } + + LOG(LOG_LEVEL_INFO, "Request to close connection : '%s'", + ccp_close_reason_to_str(reason, buff, sizeof(buff))); + xrdp_mm_set_fatal(self, errinfo); + } + + return rv; +} + +/*****************************************************************************/ +int +xrdp_mm_ccp_data_in(struct trans *trans) +{ + int rv = 0; + int available; + + rv = ccp_msg_in_check_available(trans, &available); + if (rv == 0 && available) + { + struct xrdp_mm *self = (struct xrdp_mm *)(trans->callback_data); + enum ccp_msg_code msgno; + + switch ((msgno = ccp_msg_in_get_msgno(trans))) + { + case E_CCP_CLOSE_CONNECTION_REQUEST: + rv = xrdp_mm_process_close_ccp_connection_request(self); + break; + + default: + { + char buff[64]; + ccp_msgno_to_str(msgno, buff, sizeof(buff)); + LOG(LOG_LEVEL_ERROR, "Ignored CCP message %s from sesman", + buff); + } + } + + ccp_msg_in_reset(trans); + } + + return rv; +} diff --git a/xrdp/xrdp_process.c b/xrdp/xrdp_process.c index c40613dc..854f0427 100644 --- a/xrdp/xrdp_process.c +++ b/xrdp/xrdp_process.c @@ -295,14 +295,14 @@ xrdp_process_main_loop(struct xrdp_process *self) } } /* send disconnect message if possible */ - libxrdp_disconnect(self->session); + libxrdp_disconnect(self->session, self->errinfo); } else { LOG(LOG_LEVEL_ERROR, "xrdp_process_main_loop: libxrdp_process_incoming failed"); /* this will try to send a disconnect, maybe should check that connection got far enough */ - libxrdp_disconnect(self->session); + libxrdp_disconnect(self->session, self->errinfo); } /* Run end in module */ xrdp_process_mod_end(self); diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h index 271e9077..231db9fa 100644 --- a/xrdp/xrdp_types.h +++ b/xrdp/xrdp_types.h @@ -464,6 +464,8 @@ struct xrdp_mm int last_sync_saved; int last_sync_key_flags; int last_sync_device_flags; + /* Whether the sesman_trans is a CCP trans or not */ + int sesman_trans_is_ccp; }; struct xrdp_key_info @@ -610,6 +612,7 @@ struct xrdp_process int status; struct trans *server_trans; /* in tcp server mode */ tbus self_term_event; + int errinfo; /* Reason for self_term_event being set */ struct xrdp_listen *lis_layer; /* owner */ struct xrdp_session *session; /* create these when up and running */ From b5ba5635e1152ea91325dfa9351c21f3d55e868b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 14 Jul 2025 11:02:25 +0100 Subject: [PATCH 04/10] Replace unneeded callback data value --- sesman/sesexec/eicp_server.c | 4 +--- sesman/sesexec/sesexec_discover.c | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sesman/sesexec/eicp_server.c b/sesman/sesexec/eicp_server.c index 263fd0a6..9f75ca36 100644 --- a/sesman/sesexec/eicp_server.c +++ b/sesman/sesexec/eicp_server.c @@ -172,9 +172,7 @@ handle_create_session_request(struct trans *self) if (status == 0 && scp_status == E_SCP_SCREATE_OK) { // Further comms to sesman is sent over the ERCP protocol - ercp_trans_from_eicp_trans(self, - sesexec_ercp_data_in, - (void *)self); + ercp_trans_from_eicp_trans(self, sesexec_ercp_data_in, NULL); // Announce the session to sesman if ((status = ercp_send_session_announce_event( diff --git a/sesman/sesexec/sesexec_discover.c b/sesman/sesexec/sesexec_discover.c index 952b327c..5d573aa2 100644 --- a/sesman/sesexec/sesexec_discover.c +++ b/sesman/sesexec/sesexec_discover.c @@ -83,7 +83,7 @@ discover_trans_conn_in(struct trans *trans, struct trans *new_trans) // session ercp_init_trans(new_trans); new_trans->trans_data_in = sesexec_ercp_data_in; - new_trans->callback_data = (void *)new_trans; + new_trans->callback_data = NULL; // Note, this call makes further privilege checks that may still // fail. If they do however, we wish to carry on running. These From d88cf53453cfdce3d37f68f951349455eee450db Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:11:53 +0100 Subject: [PATCH 05/10] Add CCP support to sesexec sesexec can now tell the xrdp process to exit, and is aware when the xrdp process exits. --- sesman/sesexec/Makefile.am | 2 + sesman/sesexec/ccp_server.c | 53 ++++++++++ sesman/sesexec/ccp_server.h | 39 +++++++ sesman/sesexec/ercp_server.c | 36 ++++++- sesman/sesexec/sesexec.c | 198 ++++++++++++++++++++++++++++++++++- sesman/sesexec/sesexec.h | 90 +++++++++++++++- 6 files changed, 406 insertions(+), 12 deletions(-) create mode 100644 sesman/sesexec/ccp_server.c create mode 100644 sesman/sesexec/ccp_server.h diff --git a/sesman/sesexec/Makefile.am b/sesman/sesexec/Makefile.am index ac8a2c42..d444cedf 100644 --- a/sesman/sesexec/Makefile.am +++ b/sesman/sesexec/Makefile.am @@ -21,6 +21,8 @@ xrdp_sesexec_SOURCES = \ sesexec.h \ session.c \ session.h \ + ccp_server.c \ + ccp_server.h \ eicp_server.c \ eicp_server.h \ ercp_server.c \ diff --git a/sesman/sesexec/ccp_server.c b/sesman/sesexec/ccp_server.c new file mode 100644 index 00000000..c973288f --- /dev/null +++ b/sesman/sesexec/ccp_server.c @@ -0,0 +1,53 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2023 + * + * 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. + */ + +/** + * + * @file eicp_server.c + * @brief eicp (executive initialisation control protocol) server function + * @author Matt Burt + * + */ + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include "trans.h" + +#include "ccp.h" +#include "ccp_server.h" + +/******************************************************************************/ +int +ccp_server(struct trans *self) +{ + int rv = 0; + enum ccp_msg_code msgno; + + switch ((msgno = ccp_msg_in_get_msgno(self))) + { + default: + { + char buff[64]; + ccp_msgno_to_str(msgno, buff, sizeof(buff)); + LOG(LOG_LEVEL_ERROR, "Ignored CCP message %s", buff); + } + } + return rv; +} diff --git a/sesman/sesexec/ccp_server.h b/sesman/sesexec/ccp_server.h new file mode 100644 index 00000000..83d022c8 --- /dev/null +++ b/sesman/sesexec/ccp_server.h @@ -0,0 +1,39 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2023 + * + * 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. + */ + +/** + * + * @file ccp_server.h + * @brief ccp (connection control protocol) server function + * @author Matt Burt + * + */ + +#ifndef CCP_SERVER_H +#define CCP_SERVER_H + +/** + * + * @brief Processes an CCP message + * @param self The CCP transport the message is coming in on + * + */ +int +ccp_server(struct trans *self); + +#endif // CCP_SERVER_H diff --git a/sesman/sesexec/ercp_server.c b/sesman/sesexec/ercp_server.c index 539473b0..434de3f7 100644 --- a/sesman/sesexec/ercp_server.c +++ b/sesman/sesexec/ercp_server.c @@ -87,15 +87,33 @@ handle_connect_session_request(struct trans *self) } else { - scp_fd = -1; // Don't close this twice! + // Ownership of the file descriptor is passed to scp_trans; + // don't delete it separately. + scp_fd = -1; // Now we've got a transport we can send data back to // the SCP client enum scp_sconnect_status scp_status; int display_fd = -1; int chan_fd = -1; + + // Terminate any existing xrdp process to sesexec + if (g_ccp_trans != NULL) + { + sesexec_terminate_connected_xrdp_process( + CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION); + g_sleep(500); + } scp_status = get_session_fds(g_session_data, scp_flags, &display_fd, &chan_fd); + + // Tell sesman about the new client connection + if (g_ecp_trans != NULL) + { + /// TODO: client connect event + } + + // Pass the session file descriptors to the client rv = scp_send_connect_session_response(scp_trans, scp_status, display_fd, chan_fd); @@ -120,10 +138,17 @@ handle_connect_session_request(struct trans *self) session_run_reconnect_script(g_login_info, g_session_data); } + + // Convert the SCP transport to a CCP transport, and + // record it + if (sesexec_set_ccp_trans(scp_trans) == 0) + { + scp_trans = NULL; // Prevent transport being deleted. + } } - // Regardless of the result of the send, we must close all - // our copies of file descriptors. + // Close all our copies of file descriptors, including the + // SCP transport if we failed to convert it to a CCP transport if (display_fd >= 0) { g_file_close(display_fd); @@ -132,7 +157,10 @@ handle_connect_session_request(struct trans *self) { g_file_close(chan_fd); } - trans_delete(scp_trans); + if (scp_trans != NULL) + { + trans_delete(scp_trans); + } } } diff --git a/sesman/sesexec/sesexec.c b/sesman/sesexec/sesexec.c index 2f614ff3..788b7908 100644 --- a/sesman/sesexec/sesexec.c +++ b/sesman/sesexec/sesexec.c @@ -32,6 +32,8 @@ #include #include "arch.h" +#include "ccp.h" +#include "ccp_server.h" #include "eicp.h" #include "eicp_server.h" #include "ercp.h" @@ -52,6 +54,12 @@ struct startup_params const char *sesman_ini; }; +enum +{ + MAX_ROBJS = 32, ///< Maximum number of file objects in use at any one time + XRDP_EXIT_TIMEOUT = 2500 ///< Time to wait for xrdp process to exit +}; + /* * Program-scope globals */ @@ -64,10 +72,12 @@ tintptr g_term_event = 0; tintptr g_sigchld_event = 0; pid_t g_pid; +struct trans *g_ecp_trans; +struct trans *g_ccp_trans; + /* * Module-scope globals */ -static struct trans *g_ecp_trans; static pid_t g_ecp_pid; static int g_terminate_loop = 0; static int g_terminate_status = 0; @@ -163,6 +173,27 @@ sesexec_ercp_data_in(struct trans *self) return rv; } +/******************************************************************************/ +static int +sesexec_ccp_data_in(struct trans *self) +{ + int rv; + int available; + + rv = ccp_msg_in_check_available(self, &available); + + if (rv == 0 && available) + { + if ((rv = ccp_server(self)) != 0) + { + LOG(LOG_LEVEL_ERROR, "%s: ccp_server failed", __func__); + } + ccp_msg_in_reset(self); + } + + return rv; +} + /******************************************************************************/ /** * Informs the main loop a termination signal has been received @@ -235,6 +266,12 @@ sesexec_set_ecp_transport(struct trans *t) g_ecp_pid = 0; rv = 0; } + else if (t == g_ecp_trans) + { + // This would break the memory subsystem! + LOG(LOG_LEVEL_ERROR, "%s: programming error", __func__); + rv = 1; + } else if ((rv = g_sck_get_peer_cred(t->sck, &pid, &uid, &gid)) != 0) { LOG(LOG_LEVEL_ERROR, "Can't get credentials of sesman socket [%s]", @@ -286,6 +323,19 @@ sesexec_main_loop_cleanup(void) session_data_free(g_session_data); } +/******************************************************************************/ +/** + * Close the CCP trans unconditionally + * + * Use this call if you are certain the other end has gone away + */ +static void +close_ccp_trans(void) +{ + trans_delete(g_ccp_trans); + g_ccp_trans = NULL; +} + /******************************************************************************/ /** * @@ -297,7 +347,6 @@ sesexec_main_loop(void) { int error = 0; int robjs_count; -#define MAX_ROBJS 32 intptr_t robjs[MAX_ROBJS]; g_terminate_loop = 0; @@ -323,6 +372,19 @@ sesexec_main_loop(void) } } + // CCP transport is set if we have an xrdp connection + if (g_ccp_trans != NULL) + { + error = trans_get_wait_objs(g_ccp_trans, robjs, &robjs_count); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " + "trans_get_wait_objs(CCP) failed"); + sesexec_terminate_main_loop(error); + continue; + } + } + // Add any objects from the discover module error = sesexec_discover_get_wait_objs(robjs, &robjs_count, MAX_ROBJS); if (error != 0) @@ -370,13 +432,16 @@ sesexec_main_loop(void) session_process_sigchld_event(g_session_data); if (session_was_active && !session_active(g_session_data)) { - // We've finished the session. Tell sesman and + // We've finished the session. Tell sesman, xrdp and // finish up. if (g_ecp_trans != NULL) { (void)ercp_send_session_finished_event(g_ecp_trans); } + sesexec_terminate_connected_xrdp_process( + CCP_CLOSE_LOGOFF_BY_USER); + session_data_free(g_session_data); g_session_data = NULL; sesexec_terminate_main_loop(0); @@ -411,6 +476,33 @@ sesexec_main_loop(void) } } + if (g_ccp_trans != NULL) + { + error = trans_check_wait_objs(g_ccp_trans); + if (error != 0) + { + if (g_ccp_trans->status != TRANS_STATUS_UP) + { + // xrdp has gone away. + LOG(LOG_LEVEL_INFO, "sesexec_main_loop: " + "xrdp has exited"); + // TODO: Tell sesman xrdp has exited + close_ccp_trans(); + } + else + { + // A callback has failed. This shouldn't really happen. + // Try to signal a software failure to the xrdp process + LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: " + "trans_check_wait_objs failed for CCP transport"); + sesexec_terminate_connected_xrdp_process( + CCP_CLOSE_SOFTWARE_FAILURE); + + } + continue; + } + } + error = sesexec_discover_check_wait_objs(); if (error != 0) { @@ -419,14 +511,16 @@ sesexec_main_loop(void) sesexec_terminate_main_loop(error); continue; } - } /* close sesman communications immediately */ sesexec_set_ecp_transport(NULL); + /* We should already have notified xrdp of the reason why we are + * closing, in which case this call has no effect */ + sesexec_terminate_connected_xrdp_process(CCP_CLOSE_SOFTWARE_FAILURE); + return g_terminate_status; -#undef MAX_ROBJS } /******************************************************************************/ @@ -598,3 +692,97 @@ main(int argc, char **argv) g_deinit(); return error; } + +/******************************************************************************/ +void +sesexec_terminate_connected_xrdp_process(enum ccp_close_reason_type reason) +{ + if (g_ccp_trans != NULL && g_ccp_trans->status == TRANS_STATUS_UP) + { + // Ask xrdp to exit, specifying the reason to return to + // the RDP client (if possible) + (void)ccp_send_close_connection_request(g_ccp_trans, reason); + + unsigned int start_ms = g_get_elapsed_ms(); + while (1) + { + int robjs_count = 0; + intptr_t robjs[MAX_ROBJS]; + + // How long have we been waiting for xrdp to exit? + unsigned int elapsed = g_get_elapsed_ms() - start_ms; + if (elapsed > XRDP_EXIT_TIMEOUT) + { + // A timeout has occurred + LOG(LOG_LEVEL_WARNING, + "xrdp process failed to exit after %u ms", elapsed); + break; + } + + robjs[robjs_count++] = g_term_event; + if (trans_get_wait_objs(g_ccp_trans, robjs, &robjs_count) != 0) + { + // Transport has gone away + LOG(LOG_LEVEL_WARNING, + "xrdp process exited after %u ms", elapsed); + break; + } + if (g_obj_wait(robjs, robjs_count, NULL, 0, + XRDP_EXIT_TIMEOUT - elapsed) != 0) + { + /* should not get here */ + g_sleep(100); + } + else if (g_is_wait_obj_set(g_term_event)) + { + // Get out as quickly as possible + break; + } + else + { + // This will cause trans_get_wait_objs() to return a failure + // when the end of the data on the socket is reached. + (void)trans_check_wait_objs(g_ccp_trans); + } + } + } + + close_ccp_trans(); +} + +/******************************************************************************/ +int +sesexec_set_ccp_trans(struct trans *scp_trans) +{ + int rv = 1; + pid_t pid; + + if (scp_trans == NULL) + { + close_ccp_trans(); + rv = 0; + } + else if (scp_trans == g_ccp_trans) + { + // This would break the memory subsystem! + LOG(LOG_LEVEL_ERROR, "%s: programming error", __func__); + rv = 1; + } + else if ((rv = g_sck_get_peer_cred(scp_trans->sck, &pid, NULL, NULL)) != 0) + { + LOG(LOG_LEVEL_ERROR, "Can't get credentials of xrdp socket [%s]", + g_get_strerror()); + } + else + { + // Convert the transport to a CCP transport + ccp_trans_from_scp_trans(scp_trans, + sesexec_ccp_data_in, + NULL); + trans_delete(g_ccp_trans); + g_ccp_trans = scp_trans; + rv = 0; + } + + return rv; +} diff --git a/sesman/sesexec/sesexec.h b/sesman/sesexec/sesexec.h index 7d476b2c..fa0dc2d5 100644 --- a/sesman/sesexec/sesexec.h +++ b/sesman/sesexec/sesexec.h @@ -29,6 +29,8 @@ #include +#include "ccp_application_types.h" + struct config_sesman; struct trans; struct login_info; @@ -39,15 +41,70 @@ struct session_data; #endif /* Globals */ +/** + * Pointer to config data for sesman/sesexec + */ extern struct config_sesman *g_cfg; + +/** + * DES key used to obfuscate VNC password files. + * + * This key is not documented in RFC6143, but can readily be found by + * searching VNC sources. + * + * You can also find the 'reversed' form "e84ad660c4721ae0" + * on the net, particulary for openssl one-liners to decrypt VNC + * password files. + */ extern unsigned char g_fixedkey[8]; + +/** + * Information about the logged-in user + * + * This is set when the user successfully passes authentication + */ extern struct login_info *g_login_info; + +/** + * Information about the user session (opaque type) + * + * Set when the user is succesfully logged in + */ extern struct session_data *g_session_data; +/** + * Program has received a termination event + */ extern tintptr g_term_event; + +/** + * Program has received one or more SIGCHLD events + */ extern tintptr g_sigchld_event; + +/** + * PID of sesexec process + * + * Used to detect when we are running in a form of the original process + */ extern pid_t g_pid; +/** + * EICP/ERCP transport + * + * Used to communicate with the sesman process + * + * Use sesexec_is_ecp_active() if you need to check sesman is + * truly there. + */ +extern struct trans *g_ecp_trans; + +/** + * CCP transport + * + * Used to communicate with the currently connected xrdp process + */ +extern struct trans *g_ccp_trans; /** * Callback to process incoming ERCP data @@ -55,14 +112,18 @@ extern pid_t g_pid; int sesexec_ercp_data_in(struct trans *self); -/* +/** * Check for termination + * + * @return boolean. Set if program has been asked to terminate */ int sesexec_is_term(void); -/* +/** * Terminate the sesexec main loop + * + * @param status Status to return to the OS */ void sesexec_terminate_main_loop(int status); @@ -79,12 +140,35 @@ sesexec_terminate_main_loop(int status); int sesexec_set_ecp_transport(struct trans *t); -/* +/** * Is the ECP transport still active? * * @result boolean + * + * This is intended to be used as a guard to prevent an active ECP + * transport being overwritten. Do not use it to check if sesman is + * active before sending messages, as this introduces a race condition. */ int sesexec_is_ecp_active(void); +/** + * Terminate an active xrdp process + * + * @param Reason to pass back to the xrdp process (if connected) + * + * After this call, g_ccp_trans will be NULL + */ +void +sesexec_terminate_connected_xrdp_process(enum ccp_close_reason_type reason); + +/** + * Set the CCP transport from an SCP transport + * + * This call is intended to be used at the end of a connection + * event, to record our end of the connection to the xrdp process + */ +int +sesexec_set_ccp_trans(struct trans *scp_trans); + #endif // SESEXEC_H From db50a270891d02b1b421b522b344897dc7df0900 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 17 Jul 2025 10:37:38 +0100 Subject: [PATCH 06/10] Remove unnecessary include from session.h --- sesman/sesexec/session.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sesman/sesexec/session.h b/sesman/sesexec/session.h index db5519d0..076431eb 100644 --- a/sesman/sesexec/session.h +++ b/sesman/sesexec/session.h @@ -32,7 +32,6 @@ #include "guid.h" #include "scp_application_types.h" -#include "xrdp_constants.h" struct login_info; struct proc_exit_status; From ac95cdffc32da87534dbded799220bf466454c42 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 17 Jul 2025 12:06:32 +0100 Subject: [PATCH 07/10] Rename client_info hostname to client_name This name better matches the name from [MS-RDPBCGR]. Also, the size of the UTF-8 buffer allocated for the client name is not large enough for some of the names which could potentially be passed across in UTF-16 from the client. --- common/xrdp_client_info.h | 2 +- common/xrdp_constants.h | 14 +++++++++++++- libxrdp/xrdp_sec.c | 12 ++++++------ neutrinordp/xrdp-neutrinordp.c | 2 +- xrdp/xrdp_mm.c | 4 ++-- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/common/xrdp_client_info.h b/common/xrdp_client_info.h index 02f7a348..cf018d3e 100644 --- a/common/xrdp_client_info.h +++ b/common/xrdp_client_info.h @@ -146,7 +146,7 @@ struct xrdp_client_info int op2; /* use smaller bitmap header in bitmap cache */ int desktop_cache; int use_compact_packets; /* rdp5 smaller packets */ - char hostname[32]; + char client_name[INFO_CLIENT_NAME_BYTES_UTF8]; int build; int keylayout; char username[INFO_CLIENT_MAX_CB_LEN]; diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 0b6d4a26..03c37b19 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -51,8 +51,20 @@ */ #define MAX_PEER_DESCSTRLEN (46 + 2 + 1 + 5) -#define INFO_CLIENT_NAME_BYTES 32 +/* + * Number of bytes used to send a client name in the client core data + * ([MS-RDPBCGR] 2.2.1.3.2). This is 15 characters plus a terminator in + * UTF-16 + */ +#define INFO_CLIENT_NAME_BYTES_UTF16 ((15 + 1) * 2) +/* + * Number of bytes needed to store the client name as UTF-8. It is assumed + * that the 15 Unicode characters in the name all occupy BMP codepoints + * between U+0800 and U+FFFF. These codepoints all need three octets + * in UTF-8 + */ +#define INFO_CLIENT_NAME_BYTES_UTF8 ((3 * 15) + 1) /** * Maximum length of a string including the mandatory null terminator * [MS-RDPBCGR] TS_INFO_PACKET(2.2.1.11.1.1) diff --git a/libxrdp/xrdp_sec.c b/libxrdp/xrdp_sec.c index 940d3feb..73b4846b 100644 --- a/libxrdp/xrdp_sec.c +++ b/libxrdp/xrdp_sec.c @@ -1477,7 +1477,7 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s) 2 + 2 + /* desktopWidth + desktopHeight */ \ 2 + 2 + /* colorDepth + SASSequence */ \ 4 + /* keyboardLayout */ \ - 4 + INFO_CLIENT_NAME_BYTES + /* clientBuild + clientName */ \ + 4 + INFO_CLIENT_NAME_BYTES_UTF16 + /* clientBuild + clientName */ \ 4 + 4 + 4 + /* keyboardType + keyboardSubType + keyboardFunctionKey */ \ 64 + /* imeFileName */ \ 0) @@ -1523,12 +1523,12 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s) * This should be null-terminated. Allow for the possibility it * isn't by ignoring the last two bytes and treating them as a * terminator anyway */ - in_utf16_le_fixed_as_utf8(s, (INFO_CLIENT_NAME_BYTES - 2) / 2, - client_info->hostname, - sizeof(client_info->hostname)); + in_utf16_le_fixed_as_utf8(s, (INFO_CLIENT_NAME_BYTES_UTF16 - 2) / 2, + client_info->client_name, + sizeof(client_info->client_name)); in_uint8s(s, 2); /* See above */ LOG(LOG_LEVEL_INFO, "Connected client computer name: %s", - client_info->hostname); + client_info->client_name); in_uint32_le(s, client_info->keyboard_type); /* [MS-RDPBCGR] TS_UD_CS_CORE keyboardType */ in_uint32_le(s, client_info->keyboard_subtype); /* [MS-RDPBCGR] TS_UD_CS_CORE keyboardSubType */ in_uint8s(s, 4); /* keyboardFunctionKey */ @@ -1548,7 +1548,7 @@ xrdp_sec_process_mcs_data_CS_CORE(struct xrdp_sec *self, struct stream *s) "unknown"), client_info->keylayout, client_info->build, - client_info->hostname, + client_info->client_name, client_info->keyboard_type, client_info->keyboard_subtype); diff --git a/neutrinordp/xrdp-neutrinordp.c b/neutrinordp/xrdp-neutrinordp.c index 16aa27d8..76985bba 100644 --- a/neutrinordp/xrdp-neutrinordp.c +++ b/neutrinordp/xrdp-neutrinordp.c @@ -611,7 +611,7 @@ lxrdp_set_param(struct mod *mod, const char *name, const char *value) settings = mod->inst->settings; - if (g_strcmp(name, "hostname") == 0) + if (g_strcmp(name, "client_name") == 0) { } else if (g_strcmp(name, "ip") == 0) diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 536682d3..f3ced9e6 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -5342,8 +5342,8 @@ xrdp_mm_setup_mod2(struct xrdp_mm *self) self->mod->mod_set_param(self->mod, "client_info", (const char *) (self->wm->session->client_info)); - name = self->wm->session->client_info->hostname; - self->mod->mod_set_param(self->mod, "hostname", name); + name = self->wm->session->client_info->client_name; + self->mod->mod_set_param(self->mod, "client_name", name); g_snprintf(text, 255, "%d", self->wm->session->client_info->keylayout); self->mod->mod_set_param(self->mod, "keylayout", text); if (guid_is_set(&self->guid)) From cd98b013f1b5f838d559575a6b8aae4d6262e670 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 17 Jul 2025 14:52:27 +0100 Subject: [PATCH 08/10] Add logging of connect/disconnect times on connection, client IP and name are passed from xrdp to sesman to sesexec, and then back to sesman again. xrdp-sesadmin can now access the connection data from sesman --- libipm/ercp.c | 78 ++++++++++++++++++++++++++-- libipm/ercp.h | 86 +++++++++++++++++++++++++++++-- libipm/scp.c | 52 ++++++++++++++----- libipm/scp.h | 8 +++ libipm/scp_application_types.h | 3 ++ sesman/ercp_process.c | 55 +++++++++++++++++++- sesman/scp_process.c | 7 ++- sesman/sesexec/ercp_server.c | 22 ++++++-- sesman/sesexec/sesexec.c | 15 +++++- sesman/sesexec/sesexec.h | 16 ++++++ sesman/sesexec/sesexec_discover.c | 14 +++++ sesman/session_list.c | 27 ++++++---- sesman/session_list.h | 3 ++ sesman/tools/sesadmin.c | 15 ++++++ xrdp/xrdp_mm.c | 5 +- 15 files changed, 367 insertions(+), 39 deletions(-) diff --git a/libipm/ercp.c b/libipm/ercp.c index 6602c570..a96f5170 100644 --- a/libipm/ercp.c +++ b/libipm/ercp.c @@ -43,6 +43,9 @@ msgno_to_str(unsigned short n) (n == E_ERCP_CONNECT_SESSION_REQUEST) ? "ERCP_CONNECT_SESSION_REQUEST" : + (n == E_ERCP_CLIENT_CONNECT_EVENT) ? "ERCP_CLIENT_CONNECT_EVENT" : + (n == E_ERCP_CLIENT_DISCONNECT_EVENT) ? "ERCP_CLIENT_DISCONNECT_EVENT" : + NULL; } @@ -168,7 +171,7 @@ ercp_send_session_announce_event(struct trans *trans, bpp, &guid_descriptor, start_ip_addr, - start_time); + (int64_t)start_time); } /*****************************************************************************/ @@ -237,24 +240,28 @@ ercp_send_session_finished_event(struct trans *trans) int ercp_send_connect_session_request(struct trans *trans, int scp_fd, + const char *client_ip, + const char *client_name, unsigned int scp_flags) { return libipm_msg_out_simple_send( trans, (int)E_ERCP_CONNECT_SESSION_REQUEST, - "hu", scp_fd, scp_flags); + "hssu", scp_fd, client_ip, client_name, scp_flags); } /*****************************************************************************/ - int ercp_get_connect_session_request(struct trans *trans, int *scp_fd, + const char **client_ip, + const char **client_name, unsigned int *scp_flags) { /* Intermediate values */ uint32_t i_flags; - int rv = libipm_msg_in_parse(trans, "hu", scp_fd, &i_flags); + int rv = libipm_msg_in_parse(trans, "hssu", + scp_fd, client_ip, client_name, &i_flags); if (rv == 0) { *scp_flags = i_flags; @@ -262,3 +269,66 @@ ercp_get_connect_session_request(struct trans *trans, return rv; } + +/*****************************************************************************/ + +int +ercp_send_client_connect_event(struct trans *trans, + const char *client_ip, + const char *client_name, + time_t connect_time) +{ + return libipm_msg_out_simple_send( + trans, (int)E_ERCP_CLIENT_CONNECT_EVENT, + "ssx", client_ip, client_name, (int64_t)connect_time); +} + +/*****************************************************************************/ + +int +ercp_get_client_connect_event(struct trans *trans, + const char **client_ip, + const char **client_name, + time_t *connect_time) +{ + /* Intermediate values */ + int64_t i_connect_time; + + int rv = libipm_msg_in_parse(trans, "ssx", + client_ip, client_name, &i_connect_time); + if (rv == 0) + { + *connect_time = i_connect_time; + } + + return rv; +} + +/*****************************************************************************/ + +int +ercp_send_client_disconnect_event(struct trans *trans, + time_t disconnect_time) +{ + return libipm_msg_out_simple_send( + trans, (int)E_ERCP_CLIENT_DISCONNECT_EVENT, + "x", (int64_t)disconnect_time); +} + +/*****************************************************************************/ + +int +ercp_get_client_disconnect_event(struct trans *trans, + time_t *disconnect_time) +{ + /* Intermediate values */ + int64_t i_disconnect_time; + + int rv = libipm_msg_in_parse(trans, "x", &i_disconnect_time); + if (rv == 0) + { + *disconnect_time = i_disconnect_time; + } + + return rv; +} diff --git a/libipm/ercp.h b/libipm/ercp.h index 0d8e6a08..08f6d53d 100644 --- a/libipm/ercp.h +++ b/libipm/ercp.h @@ -47,8 +47,13 @@ enum ercp_msg_code E_ERCP_SESSION_ANNOUNCE_EVENT, // sesexec -> sesman E_ERCP_SESSION_FINISHED_EVENT, // sesexec -> sesman - E_ERCP_CONNECT_SESSION_REQUEST // sesman -> sesexec - // No E_EICP_CONNECT_SESSION_RESPONSE - response sent over SCP + // A connect session request has no matching response, but if + // successful, a client connect event will be generated. + E_ERCP_CONNECT_SESSION_REQUEST, // sesman -> sesexec + // No E_ERCP_CONNECT_SESSION_RESPONSE + + E_ERCP_CLIENT_CONNECT_EVENT, // sesexec -> sesman + E_ERCP_CLIENT_DISCONNECT_EVENT // sesexec -> sesman }; /* Common facilities */ @@ -236,14 +241,18 @@ ercp_send_session_finished_event(struct trans *trans); * A response is sent directly to the SCP client, rather than back * to sesman * - * @param trans EiCP transport + * @param trans ERCP transport * @param scp_fd SCP file descriptor for a response + * @param client_ip IP address of connecting client + * @param client_name Name of connecting client (from RDP client core info) * @param scp_flags Flags from scp_send_connect_session_request() * @return != 0 for error */ int ercp_send_connect_session_request(struct trans *trans, int scp_fd, + const char *client_ip, + const char *client_name, unsigned int scp_flags); /** @@ -258,13 +267,84 @@ ercp_send_connect_session_request(struct trans *trans, * * @param trans ERCP transport * @param[out] scp_fd SCP file descriptor for a response + * @param[out] client_ip IP address of connecting client + * @param[out] client_name Name of connecting client * @param[out] scp_flags Flags from scp_send_connect_session_request() * @return != 0 for error */ int ercp_get_connect_session_request(struct trans *trans, int *scp_fd, + const char **client_ip, + const char **client_name, unsigned int *scp_flags); +/** + * Send an E_ERCP_CLIENT_CONNECT_EVENT + * + * Direction : sesexec -> sesman + * + * This request tells sesman to update its connected client information + * + * @param trans ERCP transport + * @param client_ip IP address of connecting client + * @param client_name Name of connecting client (from RDP client core info) + * @param connect_time Time at which the connect event occurred + * @return != 0 for error + */ +int +ercp_send_client_connect_event(struct trans *trans, + const char *client_ip, + const char *client_name, + time_t connect_time); + +/** + * Get an E_ERCP_CLIENT_CONNECT_EVENT + * + * Direction : sesexec -> sesman + * + * This request tells sesman to update its connected client information + * + * @param trans ERCP transport + * @param[out] client_ip IP address of connecting client + * @param[out] client_name Name of connecting client (from RDP client core info) + * @param[out] connect_time Time at which the connect event occurred + * @return != 0 for error + */ +int +ercp_get_client_connect_event(struct trans *trans, + const char **client_ip, + const char **client_name, + time_t *connect_time); + +/** + * Send an E_ERCP_CLIENT_DISCONNECT_EVENT + * + * Direction : sesexec -> sesman + * + * This request tells sesman to update its connected client information + * + * @param trans ERCP transport + * @param disconnect_time Time at which the disconnect event occurred + * @return != 0 for error + */ +int +ercp_send_client_disconnect_event(struct trans *trans, + time_t disconnect_time); + +/** + * Get an E_ERCP_CLIENT_DISCONNECT_EVENT + * + * Direction : sesexec -> sesman + * + * This request tells sesman to update its connected client information + * + * @param trans ERCP transport + * @param[out] disconnect_time Time at which the disconnect event occurred + * @return != 0 for error + */ +int +ercp_get_client_disconnect_event(struct trans *trans, + time_t *disconnect_time); #endif /* ERCP_H */ diff --git a/libipm/scp.c b/libipm/scp.c index 6e48dbd6..df992417 100644 --- a/libipm/scp.c +++ b/libipm/scp.c @@ -525,6 +525,8 @@ scp_get_create_session_response(struct trans *trans, int scp_send_connect_session_request(struct trans *trans, const struct guid *guid, + const char *client_ip, + const char *client_name, unsigned int flags) { struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; @@ -532,7 +534,7 @@ scp_send_connect_session_request(struct trans *trans, return libipm_msg_out_simple_send( trans, (int)E_SCP_CONNECT_SESSION_REQUEST, - "Bu", &guid_descriptor, flags); + "Bssu", &guid_descriptor, client_ip, client_name, flags); } /*****************************************************************************/ @@ -540,13 +542,17 @@ scp_send_connect_session_request(struct trans *trans, int scp_get_connect_session_request(struct trans *trans, struct guid *guid, + const char **client_ip, + const char **client_name, unsigned int *flags) { struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; /* Intermediate values */ uint32_t i_flags; - int rv = libipm_msg_in_parse( trans, "Bu", &guid_descriptor, &i_flags); + int rv = libipm_msg_in_parse(trans, "Bssu", + &guid_descriptor, client_ip, client_name, + &i_flags); if (rv == 0) { @@ -737,7 +743,7 @@ scp_send_list_sessions_response( rv = libipm_msg_out_simple_send( trans, (int)E_SCP_LIST_SESSIONS_RESPONSE, - "iiuyqqyxis", + "iiuyqqyxisssx", status, info->sid, info->display, @@ -745,9 +751,12 @@ scp_send_list_sessions_response( info->width, info->height, info->bpp, - info->start_time, + (int64_t)info->start_time, info->uid, - info->start_ip_addr); + info->start_ip_addr, + info->client_ip, + info->client_name, + (int64_t)info->last_connect_disconnect); } return rv; @@ -785,10 +794,13 @@ scp_get_list_sessions_response( int64_t i_start_time; int32_t i_uid; char *i_start_ip_addr; + char *i_client_ip; + char *i_client_name; + int64_t i_last_connect_disconnect; rv = libipm_msg_in_parse( trans, - "iuyqqyxis", + "iuyqqyxisssx", &i_sid, &i_display, &i_type, @@ -797,25 +809,35 @@ scp_get_list_sessions_response( &i_bpp, &i_start_time, &i_uid, - &i_start_ip_addr); + &i_start_ip_addr, + &i_client_ip, + &i_client_name, + &i_last_connect_disconnect); if (rv == 0) { /* Allocate a block of memory large enough for the * structure result, and the strings it contains */ unsigned int len = sizeof(struct scp_session_info) + - g_strlen(i_start_ip_addr) + 1; + g_strlen(i_start_ip_addr) + 1 + + g_strlen(i_client_ip) + 1 + + g_strlen(i_client_name) + 1; if ((p = (struct scp_session_info *)g_malloc(len, 1)) == NULL) { *status = E_SCP_LS_NO_MEMORY; } else { - /* Set up the string pointers in the block to point - * into the memory allocated after the block */ - p->start_ip_addr = + /* Set a pointer to access the strings after the block */ + char *memptr = (char *)p + sizeof(struct scp_session_info); - +#define COPY_STRING(ptr,src) \ + { \ + size_t len = strlen(src) + 1; \ + (ptr) = memptr; \ + memcpy(memptr, src, len); \ + memptr += len; \ + } /* Copy the data over */ p->sid = i_sid; p->display = i_display; @@ -825,7 +847,11 @@ scp_get_list_sessions_response( p->bpp = i_bpp; p->start_time = i_start_time; p->uid = i_uid; - g_strcpy(p->start_ip_addr, i_start_ip_addr); + COPY_STRING(p->start_ip_addr, i_start_ip_addr); + COPY_STRING(p->client_ip, i_client_ip); + COPY_STRING(p->client_name, i_client_name); + p->last_connect_disconnect = i_last_connect_disconnect; +#undef COPY_STRING } } } diff --git a/libipm/scp.h b/libipm/scp.h index fb68d0b8..a0179c64 100644 --- a/libipm/scp.h +++ b/libipm/scp.h @@ -421,6 +421,8 @@ scp_get_create_session_response(struct trans *trans, * * @param trans SCP transport * @param guid Session guid + * @param client_ip IP address of connecting client + * @param client_name Name of connecting client (from RDP client core info) * @param flags Flags which affect the returned FDs * @return != 0 for error * @@ -429,6 +431,8 @@ scp_get_create_session_response(struct trans *trans, int scp_send_connect_session_request(struct trans *trans, const struct guid *guid, + const char *client_ip, + const char *client_name, unsigned int flags); @@ -437,12 +441,16 @@ scp_send_connect_session_request(struct trans *trans, * * @param trans SCP transport * @param[out] guid Session guid + * @param[out] client_ip IP address of connecting client + * @param[out] client_name Name of connecting client * @param[out] flags Flags which affect the returned FDs * @return != 0 for error */ int scp_get_connect_session_request(struct trans *trans, struct guid *guid, + const char **client_ip, + const char **client_name, unsigned int *flags); /** diff --git a/libipm/scp_application_types.h b/libipm/scp_application_types.h index 1c5630dd..421aed58 100644 --- a/libipm/scp_application_types.h +++ b/libipm/scp_application_types.h @@ -59,6 +59,9 @@ struct scp_session_info time_t start_time; ///< When session was created uid_t uid; ///< Username for session char *start_ip_addr; ///< IP address of starting client + char *client_ip; ///< Current client IP + char *client_name; ///< Current client name + time_t last_connect_disconnect; ///< Time of last client connect/disconnect }; /** diff --git a/sesman/ercp_process.c b/sesman/ercp_process.c index e34ae2d6..ea1e9efc 100644 --- a/sesman/ercp_process.c +++ b/sesman/ercp_process.c @@ -91,6 +91,51 @@ process_session_finished_event(struct session_item *si) si->sesexec_trans->status = TRANS_STATUS_DOWN; } +/******************************************************************************/ +static int +process_client_connect_event(struct session_item *si) +{ + int rv; + const char *client_ip; + const char *client_name; + time_t connect_time; + + rv = ercp_get_client_connect_event(si->sesexec_trans, + &client_ip, &client_name, &connect_time); + if (rv == 0) + { + strlcpy(si->client_ip, client_ip, sizeof(si->client_ip)); + strlcpy(si->client_name, client_name, sizeof(si->client_name)); + si->last_connect_disconnect = connect_time; + LOG(LOG_LEVEL_INFO, + "sesman: Session on display :%d is connected from client '%s'", + si->display, si->client_name); + } + + return rv; +} + +/******************************************************************************/ +static int +process_client_disconnect_event(struct session_item *si) +{ + int rv; + time_t disconnect_time; + + rv = ercp_get_client_disconnect_event(si->sesexec_trans, &disconnect_time); + if (rv == 0) + { + si->client_ip[0] = '\0'; + si->client_name[0] = '\0'; + si->last_connect_disconnect = disconnect_time; + LOG(LOG_LEVEL_INFO, + "sesman: Session on display :%d has no client connection", + si->display); + } + + return rv; +} + /******************************************************************************/ int ercp_process(struct session_item *si) @@ -108,11 +153,19 @@ ercp_process(struct session_item *si) process_session_finished_event(si); break; + case E_ERCP_CLIENT_CONNECT_EVENT: + rv = process_client_connect_event(si); + break; + + case E_ERCP_CLIENT_DISCONNECT_EVENT: + rv = process_client_disconnect_event(si); + break; + default: { char buff[64]; ercp_msgno_to_str(msgno, buff, sizeof(buff)); - LOG(LOG_LEVEL_ERROR, "Ignored EICP message %s", buff); + LOG(LOG_LEVEL_ERROR, "Ignored ERCP message %s", buff); } } return rv; diff --git a/sesman/scp_process.c b/sesman/scp_process.c index 819b51c6..40b0e820 100644 --- a/sesman/scp_process.c +++ b/sesman/scp_process.c @@ -576,10 +576,13 @@ process_connect_session_request(struct scp_list_item *sli) int rv; /* Client parameters describing new session */ struct guid guid; + const char *client_ip; + const char *client_name; unsigned int flags; enum scp_sconnect_status status = E_SCP_SCONNECT_OK; - rv = scp_get_connect_session_request(sli->client_trans, &guid, &flags); + rv = scp_get_connect_session_request(sli->client_trans, &guid, + &client_ip, &client_name, &flags); if (rv == 0) { @@ -615,6 +618,8 @@ process_connect_session_request(struct scp_list_item *sli) ercp_stat = ercp_send_connect_session_request( s_item->sesexec_trans, sli->client_trans->sck, + client_ip, + client_name, flags); if (ercp_stat != 0) diff --git a/sesman/sesexec/ercp_server.c b/sesman/sesexec/ercp_server.c index 434de3f7..7702e30b 100644 --- a/sesman/sesexec/ercp_server.c +++ b/sesman/sesexec/ercp_server.c @@ -72,8 +72,10 @@ handle_connect_session_request(struct trans *self) { int scp_fd = -1; unsigned int scp_flags; - - int rv = ercp_get_connect_session_request(self, &scp_fd, &scp_flags); + const char *client_ip; + const char *client_name; + int rv = ercp_get_connect_session_request(self, &scp_fd, &client_ip, + &client_name, &scp_flags); if (rv == 0) { struct trans *scp_trans; @@ -107,10 +109,20 @@ handle_connect_session_request(struct trans *self) scp_status = get_session_fds(g_session_data, scp_flags, &display_fd, &chan_fd); - // Tell sesman about the new client connection - if (g_ecp_trans != NULL) + if (scp_status == E_SCP_SCONNECT_OK) { - /// TODO: client connect event + // Tell sesman about the new client connection + strlcpy(g_client_ip, client_ip, sizeof(g_client_ip)); + strlcpy(g_client_name, client_name, sizeof(g_client_name)); + g_last_connect_disconnect = time(NULL); + + if (g_ecp_trans != NULL) + { + (void)ercp_send_client_connect_event( + g_ecp_trans, g_client_ip, g_client_name, + g_last_connect_disconnect); + + } } // Pass the session file descriptors to the client diff --git a/sesman/sesexec/sesexec.c b/sesman/sesexec/sesexec.c index 788b7908..7d0cb259 100644 --- a/sesman/sesexec/sesexec.c +++ b/sesman/sesexec/sesexec.c @@ -74,6 +74,9 @@ pid_t g_pid; struct trans *g_ecp_trans; struct trans *g_ccp_trans; +char g_client_ip[MAX_PEER_ADDRSTRLEN]; +char g_client_name[INFO_CLIENT_NAME_BYTES_UTF8]; +time_t g_last_connect_disconnect; /* * Module-scope globals @@ -486,7 +489,17 @@ sesexec_main_loop(void) // xrdp has gone away. LOG(LOG_LEVEL_INFO, "sesexec_main_loop: " "xrdp has exited"); - // TODO: Tell sesman xrdp has exited + + g_client_ip[0] = '\0'; + g_client_name[0] = '\0'; + g_last_connect_disconnect = time(NULL); + + if (g_ecp_trans != NULL) + { + (void)ercp_send_client_disconnect_event( + g_ecp_trans, g_last_connect_disconnect); + + } close_ccp_trans(); } else diff --git a/sesman/sesexec/sesexec.h b/sesman/sesexec/sesexec.h index fa0dc2d5..55db60f8 100644 --- a/sesman/sesexec/sesexec.h +++ b/sesman/sesexec/sesexec.h @@ -30,6 +30,7 @@ #include #include "ccp_application_types.h" +#include "xrdp_constants.h" struct config_sesman; struct trans; @@ -106,6 +107,21 @@ extern struct trans *g_ecp_trans; */ extern struct trans *g_ccp_trans; +/** + * Last connected client IP address + */ +extern char g_client_ip[MAX_PEER_ADDRSTRLEN]; + +/** + * Last connected client name + */ +extern char g_client_name[INFO_CLIENT_NAME_BYTES_UTF8]; + +/** + * Last connect / disconnect time + */ +extern time_t g_last_connect_disconnect; + /** * Callback to process incoming ERCP data */ diff --git a/sesman/sesexec/sesexec_discover.c b/sesman/sesexec/sesexec_discover.c index 5d573aa2..955d6167 100644 --- a/sesman/sesexec/sesexec_discover.c +++ b/sesman/sesexec/sesexec_discover.c @@ -101,6 +101,20 @@ discover_trans_conn_in(struct trans *trans, struct trans *new_trans) &sp->guid, g_login_info->ip_addr, session_get_start_time(g_session_data)); + + // Tell semsan about the last client connect or disconnect + if (g_ccp_trans != NULL) + { + (void)ercp_send_client_connect_event(new_trans, + g_client_ip, + g_client_name, + g_last_connect_disconnect); + } + else + { + (void)ercp_send_client_disconnect_event(new_trans, + g_last_connect_disconnect); + } } } return rv; diff --git a/sesman/session_list.c b/sesman/session_list.c index 4b42992b..073b8b23 100644 --- a/sesman/session_list.c +++ b/sesman/session_list.c @@ -336,18 +336,23 @@ session_list_get_byuid(const uid_t *uid, unsigned int *cnt, unsigned int flags) if (SESSION_IN_USE(si) && (uid == NULL || *uid == si->uid)) { - (sess[index]).sid = si->sesexec_pid; - (sess[index]).display = si->display; - (sess[index]).type = si->type; - (sess[index]).height = si->start_height; - (sess[index]).width = si->start_width; - (sess[index]).bpp = si->bpp; - (sess[index]).start_time = si->start_time; - (sess[index]).uid = si->uid; - (sess[index]).start_ip_addr = g_strdup(si->start_ip_addr); + sess[index].sid = si->sesexec_pid; + sess[index].display = si->display; + sess[index].type = si->type; + sess[index].height = si->start_height; + sess[index].width = si->start_width; + sess[index].bpp = si->bpp; + sess[index].start_time = si->start_time; + sess[index].uid = si->uid; + sess[index].start_ip_addr = g_strdup(si->start_ip_addr); + sess[index].client_ip = g_strdup(si->client_ip); + sess[index].client_name = g_strdup(si->client_name); + sess[index].last_connect_disconnect = si->last_connect_disconnect; /* Check for string allocation failures */ - if ((sess[index]).start_ip_addr == NULL) + if (sess[index].start_ip_addr == NULL || + sess[index].client_ip == NULL || + sess[index].client_name == NULL) { free_session_info_list(sess, *cnt); (*cnt) = 0; @@ -390,6 +395,8 @@ free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt) for (i = 0 ; i < cnt ; ++i) { g_free(sesslist[i].start_ip_addr); + g_free(sesslist[i].client_ip); + g_free(sesslist[i].client_name); } } diff --git a/sesman/session_list.h b/sesman/session_list.h index 01296170..bca1acb4 100644 --- a/sesman/session_list.h +++ b/sesman/session_list.h @@ -68,6 +68,9 @@ struct session_item struct guid guid; char start_ip_addr[MAX_PEER_ADDRSTRLEN]; time_t start_time; + char client_ip[MAX_PEER_ADDRSTRLEN]; + char client_name[INFO_CLIENT_NAME_BYTES_UTF8]; + time_t last_connect_disconnect; }; /** diff --git a/sesman/tools/sesadmin.c b/sesman/tools/sesadmin.c index 2265c056..70074d05 100644 --- a/sesman/tools/sesadmin.c +++ b/sesman/tools/sesadmin.c @@ -151,6 +151,21 @@ print_session(const struct scp_session_info *s) { printf("\tStart IP address: %s\n", s->start_ip_addr); } + if (s->client_ip[0] != '\0' && s->client_name[0] != '\0') + { + printf("\tConnection state: connected\n"); + printf("\tConnected client IP: %s\n", s->client_ip); + printf("\tConnected client name: %s\n", s->client_name); + printf("\tConnection start time: %s\n", + ctime(&s->last_connect_disconnect)); + } + else + { + printf("\tConnection state: disconnected\n"); + printf("\tConnection end time: %s\n", + (s->last_connect_disconnect == 0) ? "-" : + ctime(&s->last_connect_disconnect)); + } g_free(username); } diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index f3ced9e6..700ffc34 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -363,7 +363,10 @@ xrdp_mm_get_session_fds(struct xrdp_mm *self) } rv = scp_send_connect_session_request(self->sesman_trans, - &self->guid, flags); + &self->guid, + self->wm->client_info->client_ip, + self->wm->client_info->client_name, + flags); return rv; } From f371876e8cdfed27b75883fa4abbac37e3e69a62 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 18 Jul 2025 11:09:54 +0100 Subject: [PATCH 09/10] Set XRDP_CLIENT_xx variables for the reconnect script --- sesman/sesexec/ercp_server.c | 11 +++++++-- sesman/sesexec/session.c | 43 ++++++++++++++++++++++++++---------- sesman/sesexec/session.h | 10 ++++++++- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/sesman/sesexec/ercp_server.c b/sesman/sesexec/ercp_server.c index 7702e30b..5da61fd5 100644 --- a/sesman/sesexec/ercp_server.c +++ b/sesman/sesexec/ercp_server.c @@ -131,6 +131,13 @@ handle_connect_session_request(struct trans *self) if (rv == 0 && scp_status == E_SCP_SCONNECT_OK) { + // Variables to pass to the reconnect script + const char *vars[] = + { + "XRDP_CLIENT_IP", g_client_ip, + "XRDP_CLIENT_NAME", g_client_name, + NULL // Terminator + }; // Don't run the reconnect script on the first connect, // unless we're configured to do so. if (session_increment_connect_count(g_session_data) == 0) @@ -140,7 +147,7 @@ handle_connect_session_request(struct trans *self) if (g_cfg->always_run_reconnect) { session_run_reconnect_script(g_login_info, - g_session_data); + g_session_data, vars); } } else @@ -148,7 +155,7 @@ handle_connect_session_request(struct trans *self) LOG(LOG_LEVEL_INFO, "User %s has reconnected to a session", g_login_info->username); session_run_reconnect_script(g_login_info, - g_session_data); + g_session_data, vars); } // Convert the SCP transport to a CCP transport, and diff --git a/sesman/sesexec/session.c b/sesman/sesexec/session.c index adac6cc9..93b9579d 100644 --- a/sesman/sesexec/session.c +++ b/sesman/sesexec/session.c @@ -189,7 +189,8 @@ dumpItemsToString(struct list *self, char *outstr, int len) /******************************************************************************/ static void start_chansrv(const struct login_info *login_info, - const struct session_parameters *s) + const struct session_parameters *s, + void *closure /* unused */) { struct list *chansrv_params = list_create(); const char *exe_path = XRDP_SBIN_PATH "/xrdp-chansrv"; @@ -227,7 +228,8 @@ start_chansrv(const struct login_info *login_info, /******************************************************************************/ static void start_window_manager(const struct login_info *login_info, - const struct session_parameters *s) + const struct session_parameters *s, + void *closure /* unused */) { char text[256]; @@ -476,7 +478,8 @@ prepare_xvnc_xserver_params(const struct session_parameters *s, /* Either execs the X server, or returns */ static void start_x_server(const struct login_info *login_info, - const struct session_parameters *s) + const struct session_parameters *s, + void *closure /* unused */) { char authfile[256]; /* The filename for storing xauth information */ char execvpparams[2048]; @@ -579,10 +582,13 @@ start_x_server(const struct login_info *login_info, * Simple helper process to fork a child and log errors */ static int fork_child( - void (*runproc)(const struct login_info *, const struct session_parameters *), + void (*runproc)(const struct login_info *, + const struct session_parameters *, + void *closure), const struct login_info *login_info, const struct session_parameters *s, - pid_t group_pid) + pid_t group_pid, + void *closure) { int pid = g_fork(); if (pid == 0) @@ -592,7 +598,7 @@ fork_child( { (void)g_setpgid(0, group_pid); } - runproc(login_info, s); + runproc(login_info, s, closure); g_exit(0); } @@ -720,7 +726,7 @@ session_start_wrapped(struct login_info *login_info, * without affecting sesexec (and vice-versa). This is particularly * important when debugging sesexec as we don't want a SIGINT in * the debugger to be passed to the children */ - display_pid = fork_child(start_x_server, login_info, s, 0); + display_pid = fork_child(start_x_server, login_info, s, 0, NULL); if (display_pid > 0) { enum xwait_status xws; @@ -756,7 +762,7 @@ session_start_wrapped(struct login_info *login_info, s->display); window_manager_pid = fork_child(start_window_manager, - login_info, s, display_pid); + login_info, s, display_pid, NULL); if (window_manager_pid < 0) { g_sigterm(display_pid); @@ -770,7 +776,7 @@ session_start_wrapped(struct login_info *login_info, s->display); chansrv_pid = fork_child(start_chansrv, login_info, - s, display_pid); + s, display_pid, NULL); sd->win_mgr = window_manager_pid; sd->x_server = display_pid; @@ -1129,7 +1135,8 @@ session_send_term(struct session_data *sd, int wait_for_all) /******************************************************************************/ static void start_reconnect_script(const struct login_info *login_info, - const struct session_parameters *s) + const struct session_parameters *s, + void *closure) { env_set_user(login_info->uid, 0, s->display, g_cfg->env_names, @@ -1139,6 +1146,17 @@ start_reconnect_script(const struct login_info *login_info, if (g_file_exist(g_cfg->reconnect_sh)) { + /* The 'closure' parameter points to a list of strings + * which need to be set in the environment for the reconnect script */ + if (closure != NULL) + { + const char **p = (const char **)closure; + while (*p != NULL && *(p + 1) != NULL) + { + (void)g_setenv(*p, *(p + 1), 1); + p += 2; + } + } LOG_DEVEL_LEAKING_FDS("reconnect script", 3, -1); LOG(LOG_LEVEL_INFO, @@ -1162,10 +1180,11 @@ start_reconnect_script(const struct login_info *login_info, /******************************************************************************/ void session_run_reconnect_script(const struct login_info *login_info, - const struct session_data *sd) + const struct session_data *sd, + const char *vars[]) { if (fork_child(start_reconnect_script, - login_info, &sd->params, sd->x_server) < 0) + login_info, &sd->params, sd->x_server, (void *)vars) < 0) { LOG(LOG_LEVEL_ERROR, "Failed to fork for session reconnection script"); } diff --git a/sesman/sesexec/session.h b/sesman/sesexec/session.h index 076431eb..2280500f 100644 --- a/sesman/sesexec/session.h +++ b/sesman/sesexec/session.h @@ -158,10 +158,18 @@ session_data_free(struct session_data *session_data); /** * Runs the reconnect script for the session + * @param login_info Login info for the session + * @param sd Session data for the session + * @param vars environment variables for the reconnect script + * + * The vars parameter points to an array of strings in pairs. The + * first string in the pair is the name of an environment variable to + * set, and the second string is the value */ void session_run_reconnect_script(const struct login_info *login_info, - const struct session_data *sd); + const struct session_data *sd, + const char *vars[]); /** * Connects a file descriptor to the display server From 159947ca9b99b855e2bb2fabad84e46b93652949 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 21 Jul 2025 11:28:34 +0100 Subject: [PATCH 10/10] Minor logging improvement --- sesman/sesexec/sesexec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sesman/sesexec/sesexec.c b/sesman/sesexec/sesexec.c index 7d0cb259..6fe12678 100644 --- a/sesman/sesexec/sesexec.c +++ b/sesman/sesexec/sesexec.c @@ -488,7 +488,8 @@ sesexec_main_loop(void) { // xrdp has gone away. LOG(LOG_LEVEL_INFO, "sesexec_main_loop: " - "xrdp has exited"); + "xrdp connection has exited (client '%s')", + g_client_name); g_client_ip[0] = '\0'; g_client_name[0] = '\0';