diff --git a/libipm/eicp.c b/libipm/eicp.c index 0e4719a8..efb021c4 100644 --- a/libipm/eicp.c +++ b/libipm/eicp.c @@ -44,6 +44,7 @@ msgno_to_str(unsigned short n) (n == E_EICP_LOGOUT_REQUEST) ? "EICP_LOGOUT_REQUEST" : (n == E_EICP_CREATE_SESSION_REQUEST) ? "EICP_CREATE_SESSION_REQUEST" : + (n == E_EICP_CREATE_SESSION_RESPONSE) ? "EICP_CREATE_SESSION_RESPONSE" : NULL; } @@ -244,6 +245,23 @@ eicp_get_sys_login_response(struct trans *trans, return rv; } +/*****************************************************************************/ +int +eicp_send_uds_login_request(struct trans *trans, + int scp_fd) +{ + return libipm_msg_out_simple_send( + trans, (int)E_EICP_UDS_LOGIN_REQUEST, "h", scp_fd); +} + +/*****************************************************************************/ +int +eicp_get_uds_login_request(struct trans *trans, + int *scp_fd) +{ + return libipm_msg_in_parse( trans, "h", scp_fd); +} + /*****************************************************************************/ int eicp_send_logout_request(struct trans *trans) @@ -255,7 +273,6 @@ eicp_send_logout_request(struct trans *trans) int eicp_send_create_session_request(struct trans *trans, - int scp_fd, unsigned int display, enum scp_session_type type, unsigned short width, @@ -267,8 +284,7 @@ eicp_send_create_session_request(struct trans *trans, return libipm_msg_out_simple_send( trans, (int)E_EICP_CREATE_SESSION_REQUEST, - "huyqqyss", - scp_fd, + "uyqqyss", display, type, width, @@ -282,7 +298,6 @@ eicp_send_create_session_request(struct trans *trans, int eicp_get_create_session_request(struct trans *trans, - int *scp_fd, unsigned int *display, enum scp_session_type *type, unsigned short *width, @@ -300,8 +315,7 @@ eicp_get_create_session_request(struct trans *trans, int rv = libipm_msg_in_parse( trans, - "huyqqyss", - scp_fd, + "uyqqyss", &i_display, &i_type, &i_width, @@ -322,3 +336,40 @@ eicp_get_create_session_request(struct trans *trans, return rv; } +/*****************************************************************************/ + +int +eicp_send_create_session_response(struct trans *trans, + enum scp_screate_status status, + const struct guid *guid) +{ + struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; + return libipm_msg_out_simple_send( + trans, (int)E_EICP_CREATE_SESSION_RESPONSE, + "iB", status, &guid_descriptor); +} + +/*****************************************************************************/ + +int +eicp_get_create_session_response(struct trans *trans, + enum scp_screate_status *status, + struct guid *guid) +{ + /* Intermediate values */ + int32_t i_status; + + const struct libipm_fsb guid_descriptor = { (void *)guid, sizeof(*guid) }; + int rv = libipm_msg_in_parse( + trans, + "iB", + &i_status, + &guid_descriptor); + + if (rv == 0) + { + *status = (enum scp_screate_status)i_status; + } + + return rv; +} diff --git a/libipm/eicp.h b/libipm/eicp.h index fa581d69..63ca37dd 100644 --- a/libipm/eicp.h +++ b/libipm/eicp.h @@ -45,11 +45,14 @@ enum eicp_msg_code E_EICP_SYS_LOGIN_REQUEST, E_EICP_SYS_LOGIN_RESPONSE, + E_EICP_UDS_LOGIN_REQUEST, + // No E_EIC_UDS_LOGIN response + E_EICP_LOGOUT_REQUEST, // No E_EICP_LOGOUT_RESPONSE - E_EICP_CREATE_SESSION_REQUEST - // No E_EICP_CREATE_SESSION_RESPONSE + E_EICP_CREATE_SESSION_REQUEST, + E_EICP_CREATE_SESSION_RESPONSE }; /* Common facilities */ @@ -204,7 +207,7 @@ eicp_send_sys_login_response(struct trans *trans, int scp_fd); /** - * Parses an incoming E_EICP_SYS_LOGIN_RESPONSE (sesexec) + * Parses an incoming E_EICP_SYS_LOGIN_RESPONSE (sesman) * * @param trans EICP transport * @param[out] is_logged_in true if the SCP client is logged in @@ -221,6 +224,37 @@ eicp_get_sys_login_response(struct trans *trans, uid_t *uid, int *scp_fd); +/** + * Send an E_EICP_UDS_LOGIN_REQUEST (sesman) + * + * @param trans EICP transport + * @param scp_fd File descriptor attached to the client + * @return != 0 for error + * + * This call is needed if the sesexec process is created after + * the UID is known already. It generates no response. It is expected to + * succeed, as sesman is expected to have already vetted the caller. + * + * The file descriptor is closed immediately after it is used by the + * recipient. + */ +int +eicp_send_uds_login_request(struct trans *trans, + int scp_fd); + + +/** + * Get a E_EICP_UDS_LOGIN_REQUEST (sesexec) + * + * @param trans EICP transport + * @param[out] scp_fd File descriptor attached to the client + * @return != 0 for error + */ +int +eicp_get_uds_login_request(struct trans *trans, + int *scp_fd); + + /** * Send an E_EICP_LOGOUT_REQUEST (sesexec) * @@ -238,7 +272,6 @@ eicp_send_logout_request(struct trans *trans); * Send an E_EICP_CREATE_SESSION_REQUEST (sesman) * * @param trans EICP transport - * @param scp_fd SCP file descriptor from sesman client * @param display X display number to use * @param type Session type * @param width Initial session width @@ -248,20 +281,17 @@ eicp_send_logout_request(struct trans *trans); * @param directory Directory to run the program in. May be "" * @return != 0 for error * - * The UID for the session comes from one of two places:- - * - The UID for a sys login request is used if one has successfully - * been executed. - * - If no sys login request is used, the UID is taken from the scp_fd + * The UID for the session must have been set by a previous call. * * Following a successful request, the session creation can be * considered to be underway. The result of this operation is - * conveyed back to the caller as an ERCP event. The caller must use - * ercp_trans_from_eicp_trans() on 'trans' to convert the transport to - * an ERCP transport to receive this (and other) session run-time events. + * conveyed back to the caller as one or more ERCP messages. The caller + * must use ercp_trans_from_eicp_trans() on 'trans' to convert the + * transport to an ERCP transport to receive this (and other) session + * run-time events. */ int eicp_send_create_session_request(struct trans *trans, - int scp_fd, unsigned int display, enum scp_session_type type, unsigned short width, @@ -275,7 +305,6 @@ eicp_send_create_session_request(struct trans *trans, * Parse an incoming E_EICP_CREATE_SESSION_REQUEST (sesexec) * * @param trans EICP transport - * @param[out] scp_fd SCP file descriptor from sesman client * @param[out] display X display number to use * @param[out] type Session type * @param[out] width Initial session width @@ -290,7 +319,6 @@ eicp_send_create_session_request(struct trans *trans, */ int eicp_get_create_session_request(struct trans *trans, - int *scp_fd, unsigned int *display, enum scp_session_type *type, unsigned short *width, @@ -299,4 +327,40 @@ eicp_get_create_session_request(struct trans *trans, const char **shell, const char **directory); +/** + * Send an E_EICP_CREATE_SESSION_RESPONSE + * + * Direction : sesexec -> sesman + * + * This event is in response to an E_EICP_CREATE_SESSION_REQUEST + * + * @param trans EICP transport + * @param status Status of creation request + * @param guid GUID of session + * @return != 0 for error + */ +int +eicp_send_create_session_response(struct trans *trans, + enum scp_screate_status status, + const struct guid *guid); + + +/** + * Parse an E_EICP_CREATE_SESSION_RESPONSE + * + * Direction : sesexec -> sesman + * + * This event is in response to an E_EICP_CREATE_SESSION_REQUEST + * + * @param trans EICP transport + * @param[out] status Status of creation request + * @param[out] guid GUID of session + * @return != 0 for error + */ +int +eicp_get_create_session_response(struct trans *trans, + enum scp_screate_status *status, + struct guid *guid); + + #endif /* EICP_H */ diff --git a/libipm/ercp.c b/libipm/ercp.c index 00858387..5fa82827 100644 --- a/libipm/ercp.c +++ b/libipm/ercp.c @@ -40,6 +40,8 @@ msgno_to_str(unsigned short n) return (n == E_ERCP_SESSION_ANNOUNCE_EVENT) ? "ERCP_SESSION_ANNOUNCE_EVENT" : (n == E_ERCP_SESSION_FINISHED_EVENT) ? "ERCP_SESSION_FINISHED_EVENT" : + + (n == E_ERCP_SESSION_RECONNECT_EVENT) ? "ERCP_SESSION_RECONNECT_EVENT" : NULL; } diff --git a/libipm/ercp.h b/libipm/ercp.h index 0f608fd3..97417ebc 100644 --- a/libipm/ercp.h +++ b/libipm/ercp.h @@ -44,10 +44,10 @@ struct guid; /* Message codes */ enum ercp_msg_code { - E_ERCP_SESSION_ANNOUNCE_EVENT, - E_ERCP_SESSION_FINISHED_EVENT, + E_ERCP_SESSION_ANNOUNCE_EVENT, // sesexec -> sesman + E_ERCP_SESSION_FINISHED_EVENT, // sesexec -> sesman - E_ERCP_SESSION_RECONNECT_EVENT + E_ERCP_SESSION_RECONNECT_EVENT // sesman -> sesexec }; /* Common facilities */ @@ -225,8 +225,6 @@ ercp_get_session_announce_event(struct trans *trans, int ercp_send_session_finished_event(struct trans *trans); - - /** * Send an E_ERCP_SESSION_RECONNECT_EVENT * diff --git a/libipm/scp_application_types.c b/libipm/scp_application_types.c index 415a8fd1..f09fba01 100644 --- a/libipm/scp_application_types.c +++ b/libipm/scp_application_types.c @@ -69,6 +69,7 @@ scp_screate_status_to_str(enum scp_screate_status n, (n == E_SCP_SCREATE_NO_DISPLAY) ? "No X displays are available" : (n == E_SCP_SCREATE_X_SERVER_FAIL) ? "X server could not be started" : (n == E_SCP_SCREATE_SESSION_FAIL) ? "Session failed immediately" : + (n == E_SCP_SCREATE_IN_PROGRESS) ? "Session creation is already in progress" : (n == E_SCP_SCREATE_GENERAL_ERROR) ? "General session creation error" : /* Default */ NULL; diff --git a/libipm/scp_application_types.h b/libipm/scp_application_types.h index dcdea680..1e1698b7 100644 --- a/libipm/scp_application_types.h +++ b/libipm/scp_application_types.h @@ -99,6 +99,7 @@ enum scp_screate_status E_SCP_SCREATE_NO_DISPLAY, ///< No X server display number is available E_SCP_SCREATE_X_SERVER_FAIL, ///< X server could not be started E_SCP_SCREATE_SESSION_FAIL, ///< The session failed quickly + E_SCP_SCREATE_IN_PROGRESS, ///< A create session request is in progress E_SCP_SCREATE_GENERAL_ERROR ///< An unspecific error has occurred }; diff --git a/sesman/Makefile.am b/sesman/Makefile.am index db7ed71b..fd6d193e 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -15,6 +15,8 @@ sbin_PROGRAMS = \ xrdp-sesman xrdp_sesman_SOURCES = \ + display_utils.c \ + display_utils.h \ eicp_process.c \ eicp_process.h \ ercp_process.c \ diff --git a/sesman/display_utils.c b/sesman/display_utils.c new file mode 100644 index 00000000..0852370d --- /dev/null +++ b/sesman/display_utils.c @@ -0,0 +1,134 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2025 + * + * 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 display_utils.c + * @brief Definition of utility calls related to display handling + * @author Matt Burt + */ + + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include "arch.h" +#include "display_utils.h" +#include "log.h" +#include "os_calls.h" +#include "sesman.h" +#include "sesman_config.h" +#include "set_int.h" +#include "xrdp_sockets.h" + +/******************************************************************************/ +/** + * + * @brief checks if there's a server running on a display + * @param display the display to check + * @return 0 if there isn't a display running, nonzero otherwise + * + */ +static int +x_server_running_check_ports(int display) +{ + char text[256]; + int x_running; + int sck; + + (void)snprintf(text, sizeof(text), X11_UNIX_SOCKET_STR, display); + x_running = g_file_exist(text); + + if (!x_running) + { + LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); + (void)snprintf(text, sizeof(text), "/tmp/.X%d-lock", display); + x_running = g_file_exist(text); + } + + if (!x_running) /* check 59xx */ + { + LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); + if ((sck = g_tcp_socket()) != -1) + { + (void)snprintf(text, sizeof(text), "59%2.2d", display); + x_running = g_tcp_bind(sck, text); + g_tcp_close(sck); + } + } + + if (!x_running) /* check 60xx */ + { + LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); + if ((sck = g_tcp_socket()) != -1) + { + (void)snprintf(text, sizeof(text), "60%2.2d", display); + x_running = g_tcp_bind(sck, text); + g_tcp_close(sck); + } + } + + if (!x_running) /* check 62xx */ + { + LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); + if ((sck = g_tcp_socket()) != -1) + { + (void)snprintf(text, sizeof(text), "62%2.2d", display); + x_running = g_tcp_bind(sck, text); + g_tcp_close(sck); + } + } + + if (x_running) + { + LOG(LOG_LEVEL_INFO, "Found X server running at %s", text); + } + + return x_running; +} + +/******************************************************************************/ +int +display_utils_get_free_display(const struct set_int *alloc_displays) +{ + int display; + + for (display = g_cfg->sess.x11_display_offset; + (unsigned int)display <= g_cfg->sess.max_display_number; + ++display) + { + // Have we already allocated this one? + if (!set_int_contains(alloc_displays, display)) + { + if (!x_server_running_check_ports(display)) + { + return display; + } + } + } + + LOG(LOG_LEVEL_ERROR, + "X server -- no display in range (%d to %d) is available", + g_cfg->sess.x11_display_offset, + g_cfg->sess.max_display_number); + + return -1; +} diff --git a/sesman/display_utils.h b/sesman/display_utils.h new file mode 100644 index 00000000..acfea60f --- /dev/null +++ b/sesman/display_utils.h @@ -0,0 +1,41 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * Copyright (C) Jay Sorg 2004-2025 + * + * 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 display_utils.h + * @brief Declaration of utility calls related to display handling + * @author Matt Burt + */ + + +#ifndef DISPLAY_UTILS_H +#define DISPLAY_UTILS_H + +struct set_int; + +/** + * @brief Gets a free display number + * + * @param Displays already allocated (or being allocated) by sesman + * @return next available display, or -1 if none. + */ +int +display_utils_get_free_display(const struct set_int *alloc_displays); + +#endif // DISPLAY_UTILS_H diff --git a/sesman/eicp_process.c b/sesman/eicp_process.c index 9dbd2759..708391c7 100644 --- a/sesman/eicp_process.c +++ b/sesman/eicp_process.c @@ -32,12 +32,15 @@ #include "eicp.h" #include "eicp_process.h" +#include "ercp.h" #include "os_calls.h" #include "scp_list.h" +#include "session_list.h" #include "scp.h" #include "sesman.h" #include "sesman_access.h" #include "sesman_config.h" +#include "guid.h" /******************************************************************************/ @@ -92,6 +95,61 @@ process_sys_login_response(struct scp_list_item *sli) return rv; } +/******************************************************************************/ +static int +process_create_session_response(struct scp_list_item *sli) +{ + struct session_item *s_item; + int display = -1; + struct guid guid; + enum scp_screate_status status; + + int rv = eicp_get_create_session_response(sli->sesexec_trans, + &status, &guid); + if (rv == 0) + { + // Create an entry on the session list for the new session + if (status == E_SCP_SCREATE_OK && + (s_item = session_list_new()) == NULL) + { + status = E_SCP_SCREATE_NO_MEMORY; + } + + if (status == E_SCP_SCREATE_OK) + { + // Further comms from sesexec comes over the ERCP + // protocol + ercp_trans_from_eicp_trans(sli->sesexec_trans, + sesman_ercp_data_in, + (void *)s_item); + + // Move the new ERCP transport over to the session list item, + // and initialise enough data so that a connection request + // can be serviced. + s_item->sesexec_trans = sli->sesexec_trans; + s_item->sesexec_pid = sli->sesexec_pid; + s_item->guid = guid; + s_item->uid = sli->uid; + s_item->display = sli->session_display; + display = s_item->display; + + // We don't use the sesexec process again + sli->sesexec_trans = NULL; + sli->sesexec_pid = 0; + } + else + { + guid_clear(&guid); + display = -1; + } + rv = scp_send_create_session_response(sli->client_trans, status, + display, &guid); + sli->create_session_in_progress = 0; + sli->session_display = -1; + } + + return rv; +} /******************************************************************************/ int eicp_process(struct scp_list_item *sli) @@ -105,6 +163,10 @@ eicp_process(struct scp_list_item *sli) rv = process_sys_login_response(sli); break; + case E_EICP_CREATE_SESSION_RESPONSE: + rv = process_create_session_response(sli); + break; + default: { char buff[64]; diff --git a/sesman/scp_list.c b/sesman/scp_list.c index c5bec094..3e96bdd2 100644 --- a/sesman/scp_list.c +++ b/sesman/scp_list.c @@ -33,6 +33,7 @@ #include "list.h" #include "os_calls.h" #include "scp_list.h" +#include "set_int.h" #include "trans.h" #define SCP_LIST_ITEM_IN_USE(sli) \ @@ -125,7 +126,7 @@ scp_list_item_new(void) { g_snprintf(result->peername, sizeof(result->peername), "unknown"); result->uid = (uid_t) -1; - + result->session_display = -1; if (!list_add_item(g_scp_list, (tintptr)result)) { g_free(result); @@ -259,3 +260,23 @@ scp_list_check_wait_objs(void) return 0; } + +/******************************************************************************/ +void +scp_list_get_create_session_displays(struct set_int *alloc_displays) +{ + int i = 0; + for (i = 0; i < g_scp_list->count; ++i) + { + struct scp_list_item *sli; + + sli = (struct scp_list_item *)list_get_item(g_scp_list, i); + + if (SCP_LIST_ITEM_IN_USE(sli) && + sli->create_session_in_progress && + sli->session_display >= 0) + { + set_int_add(alloc_displays, sli->session_display); + } + } +} diff --git a/sesman/scp_list.h b/sesman/scp_list.h index d01a09c7..0752d329 100644 --- a/sesman/scp_list.h +++ b/sesman/scp_list.h @@ -32,6 +32,8 @@ #include "xrdp_constants.h" +struct set_int; + /** * Type describing the login state of an SCP list item */ @@ -83,6 +85,8 @@ struct scp_list_item char *username; ///< Username from UID (at time of logon) char start_ip_addr[MAX_PEER_ADDRSTRLEN]; int is_admin; + int create_session_in_progress; ///< Already handling a create_session + unsigned int session_display; ///< Display allocated for create_session }; @@ -151,4 +155,12 @@ scp_list_get_wait_objs(tbus robjs[], int *robjs_count); int scp_list_check_wait_objs(void); +/** + * @brief Get all create-session displays + * + * Adds displays allocated to create-session operations to a set + */ +void +scp_list_get_create_session_displays(struct set_int *alloc_displays); + #endif // SCP_LIST_H diff --git a/sesman/scp_process.c b/sesman/scp_process.c index 03075faf..0f448632 100644 --- a/sesman/scp_process.c +++ b/sesman/scp_process.c @@ -31,15 +31,16 @@ #include "trans.h" #include "os_calls.h" #include "eicp.h" -#include "ercp.h" #include "scp.h" +#include "display_utils.h" #include "scp_process.h" #include "sesman.h" #include "sesman_access.h" #include "sesman_auth.h" #include "sesman_config.h" #include "os_calls.h" +#include "set_int.h" #include "scp_list.h" #include "session_list.h" #include "sesexec_control.h" @@ -389,13 +390,45 @@ create_xrdp_socket_path(uid_t uid) #undef RWX_PERMS } +/******************************************************************************/ +/* + * Gets a free display number + * + * We can't use displays either allocated to sessions, or being used to + * create sessions + */ +static int +get_free_display(void) +{ + int result = -1; + struct set_int *alloc_displays; + + alloc_displays = set_int_init(g_cfg->sess.x11_display_offset, + g_cfg->sess.max_display_number); + + if (alloc_displays != NULL) + { + // Get all the displays either allocated to sessions, or + // potentially assigned to sessions on the SCP list + session_list_get_session_displays(alloc_displays); + scp_list_get_create_session_displays(alloc_displays); + + // Find a free display, taking the allocated ones into account + result = display_utils_get_free_display(alloc_displays); + + set_int_delete(alloc_displays); + } + + return result; +} + /******************************************************************************/ static int process_create_session_request(struct scp_list_item *sli) { int rv; - /* Client parameters describing new session*/ + /* Client parameters describing new session */ enum scp_session_type type; unsigned short width; unsigned short height; @@ -404,8 +437,9 @@ process_create_session_request(struct scp_list_item *sli) const char *directory; struct guid guid; - int display = 0; + int display = -1; struct session_item *s_item = NULL; + int start_sesexec = (sli->sesexec_trans == NULL); int send_client_reply = 1; enum scp_screate_status status = E_SCP_SCREATE_OK; @@ -420,6 +454,10 @@ process_create_session_request(struct scp_list_item *sli) { status = E_SCP_SCREATE_NOT_LOGGED_IN; } + else if (sli->create_session_in_progress) + { + status = E_SCP_SCREATE_IN_PROGRESS; + } else { LOG(LOG_LEVEL_INFO, @@ -433,29 +471,6 @@ process_create_session_request(struct scp_list_item *sli) // Found an existing session display = s_item->display; guid = s_item->guid; - - // Tell the existing session to run the reconnect script. - // We ignore errors at this level, as any comms errors - // will be picked up in the main loop - (void)ercp_send_session_reconnect_event(s_item->sesexec_trans); - - if (sli->start_ip_addr[0] != '\0') - { - LOG( LOG_LEVEL_INFO, "++ reconnected session: username %s, " - "display :%d.0, session_pid %d, ip %s", - sli->username, display, - s_item->sesexec_pid, sli->start_ip_addr); - } - else - { - LOG(LOG_LEVEL_INFO, "++ reconnected session: username %s, " - "display :%d.0, session_pid %d", - sli->username, display, s_item->sesexec_pid); - } - - // If we created an authentication process for this SCP - // connection, close it gracefully - logout_scp_list_item(sli); } // Need to create a new session else if (g_cfg->sess.max_sessions > 0 && @@ -463,34 +478,42 @@ process_create_session_request(struct scp_list_item *sli) { status = E_SCP_SCREATE_MAX_REACHED; } - else if ((display = session_list_get_available_display()) < 0) + else if ((display = get_free_display()) < 0) { status = E_SCP_SCREATE_NO_DISPLAY; } - // Create an entry on the session list for the new session - else if ((s_item = session_list_new()) == NULL) - { - status = E_SCP_SCREATE_NO_MEMORY; - } // Create a socket dir for this user else if (create_xrdp_socket_path(sli->uid) != 0) { status = E_SCP_SCREATE_GENERAL_ERROR; } // Create a sesexec process if we don't have one (UDS login) - else if (sli->sesexec_trans == NULL && sesexec_start(sli) != 0) + else if (start_sesexec && sesexec_start(sli) != 0) { LOG(LOG_LEVEL_ERROR, "Can't start sesexec to manage session"); status = E_SCP_SCREATE_GENERAL_ERROR; } + else if (start_sesexec && + eicp_send_uds_login_request( + sli->sesexec_trans, sli->client_trans->sck) != 0) + { + // Because we started sesexec late, we needed to log it in. + // That hasn't gone too well. + LOG(LOG_LEVEL_ERROR, + "Can't set UID for sesexec process"); + status = E_SCP_SCREATE_GENERAL_ERROR; + + // Looks like sesexec is broken... + trans_delete(sli->sesexec_trans); + sli->sesexec_trans = NULL; + } else { // Pass the session create request to sesexec int eicp_stat; eicp_stat = eicp_send_create_session_request( sli->sesexec_trans, - sli->client_trans->sck, display, type, width, height, bpp, shell, directory); @@ -498,42 +521,29 @@ process_create_session_request(struct scp_list_item *sli) if (eicp_stat != 0) { LOG(LOG_LEVEL_ERROR, - "Can't ask sesexec to authenticate user"); + "Can't ask sesexec to create a session"); status = E_SCP_SCREATE_GENERAL_ERROR; + // Looks like sesexec is broken... + trans_delete(sli->sesexec_trans); + sli->sesexec_trans = NULL; } else { - // We've handed over responsibility for the - // SCP communication + // We're not sending a reply yet send_client_reply = 0; - - // Further comms from sesexec comes over the ERCP - // protocol - ercp_trans_from_eicp_trans(sli->sesexec_trans, - sesman_ercp_data_in, - (void *)s_item); - - // Move the transport over to the session list item - s_item->sesexec_trans = sli->sesexec_trans; - s_item->sesexec_pid = sli->sesexec_pid; - sli->sesexec_trans = NULL; - sli->sesexec_pid = 0; - - // Add the display to the session item so we don't try - // to allocate it to another session - s_item->display = display; + sli->create_session_in_progress = 1; + sli->session_display = display; // Reserve display } } } - // Currently a create session request is the last thing on a - // connection, and results in automatic closure - // - // We may have passed the client_trans over to sesexec. If so, - // we can't send a reply here. - sli->dispatcher_action = E_SLD_TERMINATE_SCP_CONN; if (send_client_reply) { + if (status != E_SCP_SCREATE_OK) + { + display = -1; + guid_clear(&guid); + } rv = scp_send_create_session_response(sli->client_trans, status, display, &guid); } diff --git a/sesman/sesexec/eicp_server.c b/sesman/sesexec/eicp_server.c index 210d21b7..263fd0a6 100644 --- a/sesman/sesexec/eicp_server.c +++ b/sesman/sesexec/eicp_server.c @@ -64,6 +64,13 @@ handle_sys_login_request(struct trans *self) } else { + if (g_login_info != NULL) + { + // Shouldn't get here. Prevent a memory leak. + LOG(LOG_LEVEL_WARNING, + "Asked to sys login when a login has already been made"); + login_info_free(g_login_info); + } g_login_info = login_info_sys_login_user(scp_trans, username, password, ip_addr); @@ -84,6 +91,46 @@ handle_sys_login_request(struct trans *self) return rv; } +/******************************************************************************/ +static int +handle_uds_login_request(struct trans *self) +{ + int scp_fd; + + int rv = eicp_get_uds_login_request(self, &scp_fd); + if (rv == 0) + { + struct trans *scp_trans; + scp_trans = scp_init_trans_from_fd(scp_fd, TRANS_TYPE_SERVER, + sesexec_is_term); + if (scp_trans == NULL) + { + LOG(LOG_LEVEL_ERROR, "Can't create SCP trans"); + g_file_close(scp_fd); + rv = 1; + } + else + { + if (g_login_info != NULL) + { + // Shouldn't get here. Prevent a memory leak. + LOG(LOG_LEVEL_WARNING, + "Asked to UDS login when a login has already been made"); + login_info_free(g_login_info); + } + // The following call logs errors, but these are not + // returned to the caller, as the call is expected to succeed. + if ((g_login_info = login_info_uds_login_user(scp_trans)) == NULL) + { + rv = 1; + } + trans_delete(scp_trans); + } + } + + return rv; +} + /******************************************************************************/ static int handle_logout_request(struct trans *self) @@ -97,66 +144,50 @@ handle_logout_request(struct trans *self) static int handle_create_session_request(struct trans *self) { - int scp_fd; struct session_parameters sp = {0}; int status; status = eicp_get_create_session_request( - self, &scp_fd, &sp.display, + self, &sp.display, &sp.type, &sp.width, &sp.height, &sp.bpp, &sp.shell, &sp.directory); if (status == 0) { - // Need to talk to the SCP client - struct trans *scp_trans; - scp_trans = scp_init_trans_from_fd(scp_fd, TRANS_TYPE_SERVER, - sesexec_is_term); - if (scp_trans == NULL) + enum scp_screate_status scp_status = E_SCP_SCREATE_OK; + + // Must be logged in to start a session + if (g_login_info == NULL) { - LOG(LOG_LEVEL_ERROR, "Can't create SCP trans"); - g_file_close(scp_fd); - status = 1; + scp_status = E_SCP_SCREATE_NOT_LOGGED_IN; } else { - enum scp_screate_status scp_status = E_SCP_SCREATE_OK; - - // Use the UID from the SCP connection if the user hasn't - // explicitly logged in - if (g_login_info == NULL && - (g_login_info = login_info_uds_login_user(scp_trans)) == NULL) - { - scp_status = E_SCP_SCREATE_GENERAL_ERROR; - } - - if (scp_status == E_SCP_SCREATE_OK) - { - // Try to create the session - sp.guid = guid_new(); - scp_status = session_start(g_login_info, &sp, &g_session_data); - } + // Try to create the session + sp.guid = guid_new(); + scp_status = session_start(g_login_info, &sp, &g_session_data); + } + // Return the creation status to sesman. + status = eicp_send_create_session_response(self, scp_status, &sp.guid); + 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); - if (scp_status != E_SCP_SCREATE_OK) - { - // Tell sesman the session hasn't started - (void)ercp_send_session_finished_event(self); - } - else if ((status = ercp_send_session_announce_event( - self, - sp.display, - g_login_info->uid, - sp.type, - sp.width, - sp.height, - sp.bpp, - &sp.guid, - g_login_info->ip_addr, - session_get_start_time(g_session_data))) != 0) + // Announce the session to sesman + if ((status = ercp_send_session_announce_event( + self, + sp.display, + g_login_info->uid, + sp.type, + sp.width, + sp.height, + sp.bpp, + &sp.guid, + g_login_info->ip_addr, + session_get_start_time(g_session_data))) != 0) { // We failed to tell sesman about the new session. This // probably means sesman has exited in the time between @@ -171,24 +202,15 @@ handle_create_session_request(struct trans *self) // new one LOG(LOG_LEVEL_ERROR, "sesman appears to have failed - stopping session"); - scp_status = E_SCP_SCREATE_GENERAL_ERROR; } else if ((status = sesexec_discover_enable()) != 0) { // Equally regrettable - we can't make the session - // discoverable, so we'll stop it. We can tell sesman - // about this one however. + // discoverable, so we'll stop it. LOG(LOG_LEVEL_ERROR, "unable to make session discoverable" " - stopping session"); - (void)ercp_send_session_finished_event(self); - scp_status = E_SCP_SCREATE_GENERAL_ERROR; } - - // Return the status to the SCP client. - (void)scp_send_create_session_response(scp_trans, scp_status, - sp.display, &sp.guid); - trans_delete(scp_trans); } } @@ -213,6 +235,10 @@ eicp_server(struct trans *self) rv = handle_sys_login_request(self); break; + case E_EICP_UDS_LOGIN_REQUEST: + rv = handle_uds_login_request(self); + break; + case E_EICP_LOGOUT_REQUEST: rv = handle_logout_request(self); break; diff --git a/sesman/sesexec/session.c b/sesman/sesexec/session.c index 615ffe2e..cd536467 100644 --- a/sesman/sesexec/session.c +++ b/sesman/sesexec/session.c @@ -1086,20 +1086,23 @@ session_send_term(struct session_data *sd, int wait_for_all) g_sigterm(sd->win_mgr); } - while (session_active(sd)) + if (wait_for_all) { - /* Don't check SIGTERM - we shouldn't be here long */ - if (g_obj_wait(&g_sigchld_event, 1, NULL, 0, -1) != 0) + while (session_active(sd)) { - /* should not get here */ - LOG(LOG_LEVEL_WARNING, "session_send_term: " - "Unexpected error from g_obj_wait()"); - g_sleep(100); - } - else - { - g_reset_wait_obj(g_sigchld_event); - session_process_sigchld_event(sd); + /* Don't check SIGTERM - we shouldn't be here long */ + if (g_obj_wait(&g_sigchld_event, 1, NULL, 0, -1) != 0) + { + /* should not get here */ + LOG(LOG_LEVEL_WARNING, "session_send_term: " + "Unexpected error from g_obj_wait()"); + g_sleep(100); + } + else + { + g_reset_wait_obj(g_sigchld_event); + session_process_sigchld_event(sd); + } } } } diff --git a/sesman/session_list.c b/sesman/session_list.c index 54392cf5..7ce0a2df 100644 --- a/sesman/session_list.c +++ b/sesman/session_list.c @@ -42,6 +42,7 @@ #include "log.h" #include "os_calls.h" #include "sesman.h" +#include "set_int.h" #include "string_calls.h" #include "xrdp_sockets.h" @@ -56,20 +57,19 @@ static struct list *g_session_list = NULL; int session_list_init(void) { - int rv = 1; + int rv = 0; if (g_session_list == NULL) { g_session_list = list_create_sized(g_cfg->sess.max_sessions); - } - - if (g_session_list == NULL) - { - LOG(LOG_LEVEL_ERROR, "Can't allocate session list"); - } - else - { - g_session_list->auto_free = 0; - rv = 0; + if (g_session_list == NULL) + { + LOG(LOG_LEVEL_ERROR, "Can't allocate session list"); + rv = 1; + } + else + { + g_session_list->auto_free = 0; + } } return rv; @@ -119,7 +119,7 @@ session_list_cleanup(void) unsigned int session_list_get_count(void) { - return g_session_list->count; + return (g_session_list == NULL) ? 0 : g_session_list->count; } /******************************************************************************/ @@ -159,175 +159,22 @@ session_list_new(void) } /******************************************************************************/ -/** - * - * @brief checks if there's a server running on a display - * @param display the display to check - * @return 0 if there isn't a display running, nonzero otherwise - * - */ -static int -x_server_running_check_ports(int display) +void +session_list_get_session_displays(struct set_int *alloc_displays) { - char text[256]; - int x_running; - int sck; + int count = (g_session_list == NULL) ? 0 : g_session_list->count; - g_snprintf(text, sizeof(text), X11_UNIX_SOCKET_STR, display); - x_running = g_file_exist(text); - - if (!x_running) + int i = 0; + for (i = 0 ; i < count ; ++i) { - LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - g_snprintf(text, sizeof(text), "/tmp/.X%d-lock", display); - x_running = g_file_exist(text); - } + struct session_item *si; + si = (struct session_item *)list_get_item(g_session_list, i); - if (!x_running) /* check 59xx */ - { - LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - if ((sck = g_tcp_socket()) != -1) + if (SESSION_IN_USE(si)) { - g_snprintf(text, sizeof(text), "59%2.2d", display); - x_running = g_tcp_bind(sck, text); - g_tcp_close(sck); + set_int_add(alloc_displays, si->display); } } - - if (!x_running) /* check 60xx */ - { - LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - if ((sck = g_tcp_socket()) != -1) - { - g_snprintf(text, sizeof(text), "60%2.2d", display); - x_running = g_tcp_bind(sck, text); - g_tcp_close(sck); - } - } - - if (!x_running) /* check 62xx */ - { - LOG(LOG_LEVEL_DEBUG, "Did not find a running X server at %s", text); - if ((sck = g_tcp_socket()) != -1) - { - g_snprintf(text, sizeof(text), "62%2.2d", display); - x_running = g_tcp_bind(sck, text); - g_tcp_close(sck); - } - } - - if (x_running) - { - LOG(LOG_LEVEL_INFO, "Found X server running at %s", text); - } - - return x_running; -} - -/******************************************************************************/ -/* Helper function for get_sorted_display_list():qsort() */ -static int -icmp(const void *v1, const void *v2) -{ - // Pointers point to unsigned ints - unsigned int i1 = *(unsigned int *)v1; - unsigned int i2 = *(unsigned int *)v2; - return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; -} - -/******************************************************************************/ -/** - * Get a sorted array of all the displays allocated to sessions - * @param[out] cnt Count of displays in list - * @return Allocated array of displays or NULL for no memory - * - * Result must always be freed, even if cnt == 0 - */ - -static unsigned int * -get_sorted_session_displays(unsigned int *cnt) -{ - unsigned int *displays; - - *cnt = 0; - displays = g_new(unsigned int, session_list_get_count() + 1); - if (displays == NULL) - { - LOG(LOG_LEVEL_ERROR, "Can't allocate memory for display list"); - } - else if (g_session_list != NULL) - { - int i; - - for (i = 0 ; i < g_session_list->count ; ++i) - { - const struct session_item *si; - si = (const struct session_item *)list_get_item(g_session_list, i); - if (SESSION_IN_USE(si) && si->display >= 0) - { - displays[(*cnt)++] = si->display; - } - } - qsort(displays, *cnt, sizeof(displays[0]), icmp); - } - - return displays; -} - -/******************************************************************************/ -int -session_list_get_available_display(void) -{ - int rv = -1; - unsigned int max_alloc = 0; - - // Find all displays already allocated. We do this to prevent - // unnecessary file system accesses, and also to prevent us allocating - // the same display number to two callers who call in quick - // succession i.e. if the first caller has not created its X server - // by the time we service the second request - unsigned int *allocated_displays = get_sorted_session_displays(&max_alloc); - if (allocated_displays != NULL) - { - unsigned int i = 0; - unsigned int display; - - for (display = g_cfg->sess.x11_display_offset; - display <= g_cfg->sess.max_display_number; - ++display) - { - // Have we already allocated this one? - while (i < max_alloc && display > allocated_displays[i]) - { - ++i; - } - if (i < max_alloc && display == allocated_displays[i]) - { - continue; // Already allocated - } - - if (!x_server_running_check_ports(display)) - { - break; - } - } - - g_free(allocated_displays); - - if (display > g_cfg->sess.max_display_number) - { - LOG(LOG_LEVEL_ERROR, - "X server -- no display in range (%d to %d) is available", - g_cfg->sess.x11_display_offset, - g_cfg->sess.max_display_number); - } - else - { - rv = display; - } - } - - return rv; } /******************************************************************************/ diff --git a/sesman/session_list.h b/sesman/session_list.h index a3e762f7..fa7241c5 100644 --- a/sesman/session_list.h +++ b/sesman/session_list.h @@ -34,6 +34,8 @@ #include "scp_application_types.h" #include "xrdp_constants.h" +struct set_int; + enum session_state { /** @@ -116,13 +118,12 @@ struct session_item * session_list_new(void); /** - * Get the next available display + * @brief Get all session displays * - * The display isn't reserved until the caller has allocated a new session - * (with session_list_new()) and put the new display in it. + * Adds displays allocated to sessions to a set */ -int -session_list_get_available_display(void); +void +session_list_get_session_displays(struct set_int *alloc_displays); /** *