From 74cd7d18376cca6c2249cf57b65f2ab9f8ba5d0e Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:32:00 +0100 Subject: [PATCH] Rework sesman with new files --- sesman/Makefile.am | 14 +- sesman/scp_process.c | 685 ++++++++++++++++++------------------------ sesman/scp_process.h | 4 +- sesman/sesman.c | 336 ++++++++++----------- sesman/sesman.h | 71 ++--- sesman/session_list.c | 188 +++++------- sesman/session_list.h | 120 ++++---- sesman/sig.c | 21 -- sesman/sig.h | 8 - 9 files changed, 615 insertions(+), 832 deletions(-) diff --git a/sesman/Makefile.am b/sesman/Makefile.am index a2d64f00..ab097910 100644 --- a/sesman/Makefile.am +++ b/sesman/Makefile.am @@ -16,8 +16,10 @@ sbin_PROGRAMS = \ xrdp-sesman xrdp_sesman_SOURCES = \ - env.c \ - env.h \ + eicp_process.c \ + eicp_process.h \ + ercp_process.c \ + ercp_process.h \ lock_uds.c \ lock_uds.h \ pre_session_list.c \ @@ -28,16 +30,10 @@ xrdp_sesman_SOURCES = \ sesman.h \ sesexec_control.c \ sesexec_control.h \ - session.c \ - session.h \ session_list.c \ session_list.h \ sig.c \ - sig.h \ - xauth.c \ - xauth.h \ - xwait.c \ - xwait.h + sig.h xrdp_sesman_LDADD = \ $(top_builddir)/sesman/libsesman/libsesman.la \ diff --git a/sesman/scp_process.c b/sesman/scp_process.c index 0d69f810..f2d0b976 100644 --- a/sesman/scp_process.c +++ b/sesman/scp_process.c @@ -30,168 +30,37 @@ #include "trans.h" #include "os_calls.h" +#include "eicp.h" +#include "ercp.h" #include "scp.h" -#include "sesman_config.h" #include "scp_process.h" +#include "sesman.h" #include "sesman_access.h" #include "sesman_auth.h" -#include "guid.h" +#include "sesman_config.h" #include "os_calls.h" +#include "pre_session_list.h" #include "session_list.h" -#include "session.h" -#include "sesman.h" +#include "sesexec_control.h" #include "string_calls.h" -/**************************************************************************//** - * Logs an authentication failure message - * - * @param username Username - * @param ip_addr IP address, if known - * - * The message is intended for use by fail2ban. Make changes with care. - */ -static void -log_authfail_message(const char *username, const char *ip_addr) -{ - if (ip_addr == NULL || ip_addr[0] == '\0') - { - ip_addr = "unknown"; - } - LOG(LOG_LEVEL_INFO, "AUTHFAIL: user=%s ip=%s time=%d", - username, ip_addr, g_time1()); -} - -/******************************************************************************/ - -/** - * Mode parameter for authenticate_and_authorize_connection() - */ -enum login_mode -{ - AM_SYSTEM, - AM_UDS -}; - -/** - * Authenticate and authorize the connection - * - * @param sc Connection to sesman - * @param login_mode Describes the type of login in use - * @param uid UID for user - * @param username Name for user - * @param password Password (AM_SYSTEM) or NULL. - * @param ip_addr Remote IP address (AM_SYSTEM) or NULL. - * @return Status for the operation - * - * @pre sc->auth_info, sc->username and sc->ip_addr must be NULL - * - * @post If E_SCP_LOGIN_OK is returned, sc->auth_info is non-NULL - * @post If E_SCP_LOGIN_OK is returned, sc->username is non-NULL - * @post If E_SCP_LOGIN_OK is returned, sc->ip_addr is non-NULL - * - */ -static enum scp_login_status -authenticate_and_authorize_connection(struct sesman_con *sc, - enum login_mode login_mode, - int uid, - const char *username, - const char *password, - const char *ip_addr) -{ - enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR; - struct auth_info *auth_info = NULL; - - /* Check preconditions */ - if (sc->auth_info != NULL || sc->username != NULL || sc->ip_addr != NULL) - { - LOG(LOG_LEVEL_ERROR, - "Internal error - connection already logged in"); - } - else - { - switch (login_mode) - { - case AM_SYSTEM: - auth_info = auth_userpass(username, password, ip_addr, &status); - break; - case AM_UDS: - auth_info = auth_uds(username, &status); - break; - default: - LOG(LOG_LEVEL_ERROR, "%s called with invalid mode %d", - __func__, (int)login_mode); - } - - if (auth_info != NULL) - { - if (status != E_SCP_LOGIN_OK) - { - /* This shouldn't happen */ - LOG(LOG_LEVEL_ERROR, - "Unexpected status return %d from auth call", - (int)status); - } - else if (!access_login_allowed(&g_cfg->sec, username)) - { - status = E_SCP_LOGIN_NOT_AUTHORIZED; - LOG(LOG_LEVEL_INFO, "Username okay but group problem for " - "user: %s", username); - } - - /* If all is well, put the auth_info in the sesman connection - * for later use. If not, remove the auth_info */ - if (status == E_SCP_LOGIN_OK) - { - char *dup_username = g_strdup(username); - char *dup_ip_addr = - (ip_addr == NULL) ? g_strdup("") : g_strdup(ip_addr); - - if (dup_username == NULL || dup_ip_addr == NULL) - { - LOG(LOG_LEVEL_ERROR, "%s : Memory allocation failed", - __func__); - g_free(dup_username); - g_free(dup_ip_addr); - status = E_SCP_LOGIN_NO_MEMORY; - } - else - { - LOG(LOG_LEVEL_INFO, "Access permitted for user=%s uid=%d", - username, uid); - sc->auth_info = auth_info; - sc->uid = uid; - sc->username = dup_username; - sc->ip_addr = dup_ip_addr; - } - } - - if (status != E_SCP_LOGIN_OK) - { - auth_end(auth_info); - } - } - } - - return status; -} - /******************************************************************************/ static int -process_set_peername_request(struct sesman_con *sc) +process_set_peername_request(struct pre_session_item *psi) { int rv; const char *peername; - rv = scp_get_set_peername_request(sc->t, &peername); + rv = scp_get_set_peername_request(psi->client_trans, &peername); if (rv == 0) { - if (sesman_set_connection_peername(sc, peername) != 0) + if (pre_session_list_set_peername(psi, peername) != 0) { LOG(LOG_LEVEL_WARNING, "Failed to set connection peername from %s to %s", - sc->peername, peername); + psi->peername, peername); } } @@ -199,68 +68,142 @@ process_set_peername_request(struct sesman_con *sc) } /******************************************************************************/ -/** - * Allocates a chain item and starts the session - */ -static enum scp_screate_status -allocate_and_start_session(struct auth_info *auth_info, - const char *username, - const char *ip_addr, - const struct session_parameters *params) +static int +process_sys_login_request(struct pre_session_item *psi) { - int pid = 0; - enum scp_screate_status status; - struct session_item *si; + int rv; + const char *username; + const char *password; + const char *ip_addr; + int send_client_reply = 1; - /* check to limit concurrent sessions */ - if (session_list_get_count() >= (unsigned int)g_cfg->sess.max_sessions) + rv = scp_get_sys_login_request(psi->client_trans, &username, + &password, &ip_addr); + if (rv == 0) { - LOG(LOG_LEVEL_ERROR, "max concurrent session limit " - "exceeded. login for user %s denied", username); - return E_SCP_SCREATE_MAX_REACHED; - } + enum scp_login_status errorcode; - si = session_new(); - if (si == NULL) - { - LOG(LOG_LEVEL_ERROR, "Out of memory error: cannot create new session " - "element - user %s", username); - return E_SCP_SCREATE_NO_MEMORY; - } + LOG(LOG_LEVEL_INFO, + "Received system login request from %s for user: %s IP: %s", + psi->peername, username, ip_addr); - status = session_start(auth_info, params, &pid); - if (status == E_SCP_SCREATE_OK) - { - if (ip_addr[0] != '\0') + if (psi->login_state != E_PS_LOGIN_NOT_LOGGED_IN) { - LOG(LOG_LEVEL_INFO, "++ created session: username %s, ip %s", - username, ip_addr); + errorcode = E_SCP_LOGIN_ALREADY_LOGGED_IN; + LOG(LOG_LEVEL_ERROR, "Connection is already logged in for %s", + psi->username); + } + else if ((psi->username = g_strdup(username)) == NULL) + { + errorcode = E_SCP_LOGIN_NO_MEMORY; + LOG(LOG_LEVEL_ERROR, "Memory allocation failure logging in %s", + username); } else { - LOG(LOG_LEVEL_INFO, "++ created session: username %s", username); + /* Create a sesexec process to handle the login + * + * We won't check for the user being valid here, as this might + * lead to information leakage */ + if (sesexec_start(psi) != 0) + { + LOG(LOG_LEVEL_ERROR, + "Can't start sesexec to authenticate user"); + errorcode = E_SCP_LOGIN_GENERAL_ERROR; + } + else + { + int eicp_stat; + eicp_stat = eicp_send_sys_login_request(psi->sesexec_trans, + username, + password, + ip_addr, + psi->client_trans->sck); + if (eicp_stat != 0) + { + LOG(LOG_LEVEL_ERROR, + "Can't ask sesexec to authenticate user"); + errorcode = E_SCP_LOGIN_GENERAL_ERROR; + } + else + { + /* We've handed over responsibility for the + * SCP communication */ + send_client_reply = 0; + psi->dispatcher_action = E_PSD_REMOVE_CLIENT_TRANS; + } + } } - si->pid = pid; - si->display = params->display; - si->width = params->width; - si->height = params->height; - si->bpp = params->bpp; - si->auth_info = auth_info; - g_strncpy(si->start_ip_addr, ip_addr, - sizeof(si->start_ip_addr) - 1); - si->uid = params->uid; - si->guid = params->guid; - - si->start_time = g_time1(); - - si->type = params->type; - si->status = SESMAN_SESSION_STATUS_ACTIVE; + if (send_client_reply) + { + /* We only get here if something has gone + * wrong with the handover to sesexec */ + rv = scp_send_login_response(psi->client_trans, errorcode, 1); + psi->dispatcher_action = E_PSD_TERMINATE_PRE_SESSION; + } } - else + + return rv; +} + +/******************************************************************************/ + +/** + * Authenticate and authorize a UDS connection + * + * @param psi Connection to sesman + * @param uid UID for user + * @param username Name for user + * @return Status for the operation + * + * @post If E_SCP_LOGIN_OK is returned, psi->username is non-NULL + */ +static enum scp_login_status +authenticate_and_authorize_uds_connection(struct pre_session_item *psi, + int uid, + const char *username) +{ + enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR; + struct auth_info *auth_info = auth_uds(username, &status); + if (auth_info != NULL) { - // Remove session item from the list - session_list_kill(-1); + if (status != E_SCP_LOGIN_OK) + { + /* This shouldn't happen */ + LOG(LOG_LEVEL_ERROR, + "Unexpected status return %d from auth_uds call", + (int)status); + } + else if (!access_login_allowed(&g_cfg->sec, username)) + { + status = E_SCP_LOGIN_NOT_AUTHORIZED; + LOG(LOG_LEVEL_INFO, "Username okay but group problem for " + "user: %s", username); + } + + /* If all is well, add info to the sesman connection for later use */ + if (status == E_SCP_LOGIN_OK) + { + if ((psi->username = g_strdup(username)) == NULL) + { + LOG(LOG_LEVEL_ERROR, "%s : Memory allocation failed", + __func__); + g_free(psi->username); + psi->username = NULL; + status = E_SCP_LOGIN_NO_MEMORY; + } + else + { + LOG(LOG_LEVEL_INFO, "Access permitted for user: %s", + username); + psi->login_state = E_PS_LOGIN_UDS; + psi->uid = uid; + psi->start_ip_addr[0] = '\0'; + } + } + + auth_end(auth_info); } return status; @@ -269,101 +212,7 @@ allocate_and_start_session(struct auth_info *auth_info, /******************************************************************************/ static int -process_sys_login_request(struct sesman_con *sc) -{ - int rv; - const char *supplied_username; - const char *password; - const char *ip_addr; - - rv = scp_get_sys_login_request(sc->t, &supplied_username, - &password, &ip_addr); - if (rv == 0) - { - enum scp_login_status errorcode; - int server_closed = 1; - int uid; - char *username = NULL; - - LOG(LOG_LEVEL_INFO, - "Received system login request from %s for user: %s IP: %s", - sc->peername, supplied_username, ip_addr); - - if (sc->auth_info != NULL) - { - errorcode = E_SCP_LOGIN_ALREADY_LOGGED_IN; - LOG(LOG_LEVEL_ERROR, "Connection is already logged in for %s", - sc->username); - } - else if (g_getuser_info_by_name(supplied_username, - &uid, NULL, NULL, NULL, NULL) != 0) - { - /* we can't get a UID for the user */ - errorcode = E_SCP_LOGIN_NOT_AUTHENTICATED; - LOG(LOG_LEVEL_ERROR, "Can't get UID for user %s", - supplied_username); - log_authfail_message(username, ip_addr); - } - else if (g_getuser_info_by_uid(uid, - &username, NULL, NULL, NULL, NULL) != 0) - { - errorcode = E_SCP_LOGIN_GENERAL_ERROR; - LOG(LOG_LEVEL_ERROR, "Can't reverse lookup UID %d", uid); - } - else - { - if (g_strcmp(supplied_username, username) != 0) - { - /* - * If using a federated naming service (e.g. AD), the - * username supplied may not match that name mapped to by - * the UID. We will generate a warning in this instance so - * the user can see what is being used within sesman - */ - LOG(LOG_LEVEL_WARNING, - "Using username %s for the session (from UID %d)", - username, uid); - } - - errorcode = authenticate_and_authorize_connection( - sc, AM_SYSTEM, - uid, username, - password, ip_addr); - if (errorcode == E_SCP_LOGIN_OK) - { - server_closed = 0; - } - else if (errorcode == E_SCP_LOGIN_NOT_AUTHENTICATED) - { - log_authfail_message(username, ip_addr); - if (sc->auth_retry_count > 0) - { - /* Password problem? Invite the user to retry */ - server_closed = 0; - --sc->auth_retry_count; - } - } - - g_free(username); - } - - if (server_closed) - { - /* Expecting no more client messages. Close the connection - * after returning from this callback */ - sc->close_requested = 1; - } - - rv = scp_send_login_response(sc->t, errorcode, server_closed); - } - - return rv; -} - -/******************************************************************************/ - -static int -process_uds_login_request(struct sesman_con *sc) +process_uds_login_request(struct pre_session_item *psi) { enum scp_login_status errorcode; int rv; @@ -372,25 +221,25 @@ process_uds_login_request(struct sesman_con *sc) char *username = NULL; int server_closed = 1; - rv = g_sck_get_peer_cred(sc->t->sck, &pid, &uid, NULL); + rv = g_sck_get_peer_cred(psi->client_trans->sck, &pid, &uid, NULL); if (rv != 0) { LOG(LOG_LEVEL_INFO, "Unable to get peer credentials for socket %d", - (int)sc->t->sck); + (int)psi->client_trans->sck); errorcode = E_SCP_LOGIN_GENERAL_ERROR; } else { LOG(LOG_LEVEL_INFO, "Received UDS login request from %s for UID: %d from PID: %d", - sc->peername, uid, pid); + psi->peername, uid, pid); - if (sc->auth_info != NULL) + if (psi->login_state != E_PS_LOGIN_NOT_LOGGED_IN) { errorcode = E_SCP_LOGIN_ALREADY_LOGGED_IN; LOG(LOG_LEVEL_ERROR, "Connection is already logged in for %s", - sc->username); + psi->username); } else if (g_getuser_info_by_uid(uid, &username, NULL, NULL, NULL, NULL) != 0) @@ -400,10 +249,8 @@ process_uds_login_request(struct sesman_con *sc) } else { - errorcode = authenticate_and_authorize_connection( - sc, AM_UDS, - uid, username, - NULL, NULL); + errorcode = authenticate_and_authorize_uds_connection( + psi, uid, username); g_free(username); if (errorcode == E_SCP_LOGIN_OK) @@ -416,27 +263,40 @@ process_uds_login_request(struct sesman_con *sc) if (server_closed) { /* Close the connection after returning from this callback */ - sc->close_requested = 1; + psi->dispatcher_action = E_PSD_TERMINATE_PRE_SESSION; } - return scp_send_login_response(sc->t, errorcode, server_closed); + return scp_send_login_response(psi->client_trans, errorcode, server_closed); +} + +/******************************************************************************/ + +static void +logout_pre_session(struct pre_session_item *psi) +{ + if (psi->login_state != E_PS_LOGIN_NOT_LOGGED_IN) + { + (void)eicp_send_logout_request(psi->sesexec_trans); + trans_delete(psi->sesexec_trans); + psi->sesexec_trans = NULL; + psi->uid = (uid_t) -1; + g_free(psi->username); + psi->username = NULL; + psi->start_ip_addr[0] = '\0'; + + psi->login_state = E_PS_LOGIN_NOT_LOGGED_IN; + } } /******************************************************************************/ static int -process_logout_request(struct sesman_con *sc) +process_logout_request(struct pre_session_item *psi) { - if (sc->auth_info != NULL) + if (psi->login_state != E_PS_LOGIN_NOT_LOGGED_IN) { - LOG(LOG_LEVEL_INFO, "Logging out %s from sesman", sc->username); - auth_end(sc->auth_info); - sc->auth_info = NULL; - sc->uid = -1; - g_free(sc->username); - sc->username = NULL; - g_free(sc->ip_addr); - sc->ip_addr = NULL; + LOG(LOG_LEVEL_INFO, "Logging out %s from sesman", psi->username); + logout_pre_session(psi); } return 0; @@ -445,117 +305,146 @@ process_logout_request(struct sesman_con *sc) /******************************************************************************/ static int -process_create_session_request(struct sesman_con *sc) +process_create_session_request(struct pre_session_item *psi) { int rv; - // Parameters for a new session (if required). Filled in as - // we go along. - struct session_parameters sp = {0}; - const char *shellptr; - const char *dirptr; + /* Client parameters describing new session*/ + enum scp_session_type type; + unsigned short width; + unsigned short height; + unsigned char bpp; + const char *shell; + const char *directory; + + struct guid guid; + int display = 0; + struct session_item *s_item = NULL; + int send_client_reply = 1; + enum scp_screate_status status = E_SCP_SCREATE_OK; - int display = 0; - struct guid guid; - - guid_clear(&guid); - - rv = scp_get_create_session_request(sc->t, - &sp.type, &sp.width, &sp.height, - &sp.bpp, &shellptr, &dirptr); + rv = scp_get_create_session_request(psi->client_trans, + &type, &width, &height, + &bpp, &shell, &directory); if (rv == 0) { - if (sc->auth_info == NULL) + if (psi->login_state == E_PS_LOGIN_NOT_LOGGED_IN) { status = E_SCP_SCREATE_NOT_LOGGED_IN; } else { LOG(LOG_LEVEL_INFO, - "Received request from %s to create a session for user %s" - " type=%s" - " geometry=%dx%d, bpp=%d, shell=\"%s\", dir=\"%s\"", - sc->peername, sc->username, - SCP_SESSION_TYPE_TO_STR(sp.type), - sp.width, sp.height, sp.bpp, shellptr, dirptr); + "Received request from %s to create a session for user %s", + psi->peername, psi->username); - struct session_item *s_item = - session_list_get_bydata(sc->uid, sp.type, sp.width, sp.height, - sp.bpp, sc->ip_addr); - if (s_item != 0) + s_item = session_list_get_bydata(psi->uid, type, width, height, + bpp, psi->start_ip_addr); + if (s_item != NULL) { // Found an existing session - if (sc->ip_addr[0] != '\0') + 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 (psi->start_ip_addr[0] != '\0') { LOG( LOG_LEVEL_INFO, "++ reconnected session: username %s, " "display :%d.0, session_pid %d, ip %s", - sc->username, s_item->display, s_item->pid, - sc->ip_addr); + psi->username, display, + s_item->sesexec_pid, psi->start_ip_addr); } else { LOG(LOG_LEVEL_INFO, "++ reconnected session: username %s, " "display :%d.0, session_pid %d", - sc->username, s_item->display, s_item->pid); + psi->username, display, s_item->sesexec_pid); } - // Get values for response to SCP client - display = s_item->display; - guid = s_item->guid; - - session_reconnect(s_item->display, sc->uid, sc->auth_info); + // If we created an authentication process for this SCP + // connection, close it gracefully + logout_pre_session(psi); + } + // Need to create a new session + else if (g_cfg->sess.max_sessions > 0 && + session_list_get_count() >= g_cfg->sess.max_sessions) + { + status = E_SCP_SCREATE_MAX_REACHED; + } + else if ((display = session_list_get_available_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 sesexec process if we don't have one (UDS login) + else if (psi->sesexec_trans == NULL && sesexec_start(psi) != 0) + { + LOG(LOG_LEVEL_ERROR, + "Can't start sesexec to authenticate user"); + status = E_SCP_SCREATE_GENERAL_ERROR; } else { - // Need to create a new session - // - // Get the rest of the parameters for the session - guid = guid_new(); - display = session_list_get_available_display(); + // Pass the session create request to sesexec + int eicp_stat; + eicp_stat = eicp_send_create_session_request( + psi->sesexec_trans, + psi->client_trans->sck, + display, + type, width, height, + bpp, shell, directory); - sp.display = display; - sp.uid = sc->uid; - sp.guid = guid; - // These need to be copied so they are available - // when the sub-process closes all the connections - g_snprintf(sp.shell, sizeof(sp.shell), "%s", shellptr); - g_snprintf(sp.directory, sizeof(sp.directory), "%s", dirptr); - - if (display == 0) + if (eicp_stat != 0) { - status = E_SCP_SCREATE_NO_DISPLAY; + LOG(LOG_LEVEL_ERROR, + "Can't ask sesexec to authenticate user"); + status = E_SCP_SCREATE_GENERAL_ERROR; } else { - // The new session will have a lifetime longer than - // the sesman connection, and so needs to own - // the auth_info struct. - // - // Copy the auth_info struct out of the connection and pass - // it to the session - struct auth_info *auth_info = sc->auth_info; - sc->auth_info = NULL; + // We've handed over responsibility for the + // SCP communication + send_client_reply = 0; - status = allocate_and_start_session(auth_info, - sc->username, - sc->ip_addr, - &sp); - if (status != E_SCP_SCREATE_OK) - { - // Close the auth session down as it can't be re-used. - auth_end(auth_info); - } + // Further comms from sesexec comes over the ERCP + // protocol + ercp_trans_from_eicp_trans(psi->sesexec_trans, + sesman_ercp_data_in, + (void *)s_item); + + // Move the transport over to the session list item + s_item->sesexec_trans = psi->sesexec_trans; + s_item->sesexec_pid = psi->sesexec_pid; + psi->sesexec_trans = NULL; + psi->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; } } } - /* Currently a create session request is the last thing on a - * connection, and results in automatic closure */ - sc->close_requested = 1; - - rv = scp_send_create_session_response(sc->t, status, - display, &guid); + // 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. + psi->dispatcher_action = E_PSD_TERMINATE_PRE_SESSION; + if (send_client_reply) + { + rv = scp_send_create_session_response(psi->client_trans, + status, display, &guid); + } } return rv; @@ -564,7 +453,7 @@ process_create_session_request(struct sesman_con *sc) /******************************************************************************/ static int -process_list_sessions_request(struct sesman_con *sc) +process_list_sessions_request(struct pre_session_item *psi) { int rv = 0; @@ -572,9 +461,9 @@ process_list_sessions_request(struct sesman_con *sc) unsigned int cnt = 0; unsigned int i; - if (sc->auth_info == NULL) + if (psi->login_state == E_PS_LOGIN_NOT_LOGGED_IN) { - rv = scp_send_list_sessions_response(sc->t, + rv = scp_send_list_sessions_response(psi->client_trans, E_SCP_LS_NOT_LOGGED_IN, NULL); } @@ -582,14 +471,13 @@ process_list_sessions_request(struct sesman_con *sc) { LOG(LOG_LEVEL_INFO, "Received request from %s to list sessions for user %s", - sc->peername, sc->username); + psi->peername, psi->username); - info = session_list_get_byuid(sc->uid, &cnt, - SESMAN_SESSION_STATUS_ALL); + info = session_list_get_byuid(psi->uid, &cnt, 0); for (i = 0; rv == 0 && i < cnt; ++i) { - rv = scp_send_list_sessions_response(sc->t, + rv = scp_send_list_sessions_response(psi->client_trans, E_SCP_LS_SESSION_INFO, &info[i]); } @@ -597,7 +485,7 @@ process_list_sessions_request(struct sesman_con *sc) if (rv == 0) { - rv = scp_send_list_sessions_response(sc->t, + rv = scp_send_list_sessions_response(psi->client_trans, E_SCP_LS_END_OF_LIST, NULL); } @@ -609,54 +497,54 @@ process_list_sessions_request(struct sesman_con *sc) /******************************************************************************/ static int -process_close_connection_request(struct sesman_con *sc) +process_close_connection_request(struct pre_session_item *psi) { int rv = 0; LOG(LOG_LEVEL_INFO, "Received request to close connection from %s", - sc->peername); + psi->peername); /* Expecting no more client messages. Close the connection * after returning from this callback */ - sc->close_requested = 1; + psi->dispatcher_action = E_PSD_TERMINATE_PRE_SESSION; return rv; } /******************************************************************************/ int -scp_process(struct sesman_con *sc) +scp_process(struct pre_session_item *psi) { enum scp_msg_code msgno; int rv = 0; - switch ((msgno = scp_msg_in_get_msgno(sc->t))) + switch ((msgno = scp_msg_in_get_msgno(psi->client_trans))) { case E_SCP_SET_PEERNAME_REQUEST: - rv = process_set_peername_request(sc); + rv = process_set_peername_request(psi); break; case E_SCP_SYS_LOGIN_REQUEST: - rv = process_sys_login_request(sc); + rv = process_sys_login_request(psi); break; case E_SCP_UDS_LOGIN_REQUEST: - rv = process_uds_login_request(sc); + rv = process_uds_login_request(psi); break; case E_SCP_LOGOUT_REQUEST: - rv = process_logout_request(sc); + rv = process_logout_request(psi); break; case E_SCP_CREATE_SESSION_REQUEST: - rv = process_create_session_request(sc); + rv = process_create_session_request(psi); break; case E_SCP_LIST_SESSIONS_REQUEST: - rv = process_list_sessions_request(sc); + rv = process_list_sessions_request(psi); break; case E_SCP_CLOSE_CONNECTION_REQUEST: - rv = process_close_connection_request(sc); + rv = process_close_connection_request(psi); break; default: @@ -668,3 +556,4 @@ scp_process(struct sesman_con *sc) } return rv; } + diff --git a/sesman/scp_process.h b/sesman/scp_process.h index f53875c2..9305bfa3 100644 --- a/sesman/scp_process.h +++ b/sesman/scp_process.h @@ -27,7 +27,7 @@ #ifndef SCP_PROCESS_H #define SCP_PROCESS_H -struct sesman_con; +struct pre_session_item; /** * @@ -36,6 +36,6 @@ struct sesman_con; * */ int -scp_process(struct sesman_con *sc); +scp_process(struct pre_session_item *sc); #endif diff --git a/sesman/sesman.c b/sesman/sesman.c index 8b122dc4..312574c8 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -35,23 +35,26 @@ #include "sesman_auth.h" #include "sesman_config.h" +#include "eicp.h" +#include "eicp_process.h" +#include "ercp.h" +#include "ercp_process.h" +#include "pre_session_list.h" #include "session_list.h" #include "lock_uds.h" #include "os_calls.h" #include "scp.h" #include "scp_process.h" +#include "sesexec_control.h" #include "sig.h" #include "string_calls.h" #include "trans.h" #include "xrdp_configure_options.h" /** - * Maximum number of short-lived connections to sesman - * - * At the moment, all connections to sesman are short-lived. This may change - * in the future + * Maximum number of pre-session items */ -#define MAX_SHORT_LIVED_CONNECTIONS 16 +#define MAX_PRE_SESSION_ITEMS 16 /** * Define the mode of operation of the program @@ -74,7 +77,6 @@ struct sesman_startup_params }; struct config_sesman *g_cfg; -unsigned char g_fixedkey[8] = { 23, 82, 107, 6, 35, 78, 88, 7 }; static tintptr g_term_event = 0; static tintptr g_sigchld_event = 0; static tintptr g_reload_event = 0; @@ -114,68 +116,6 @@ static int nocase_matches(const char *candidate, ...) return result; } -/** - * Allocates a sesman_con struct - * - * @param trans Pointer to newly-allocated transport - * @return struct sesman_con pointer - */ -static struct sesman_con * -alloc_connection(struct trans *t) -{ - struct sesman_con *result; - - if ((result = g_new0(struct sesman_con, 1)) != NULL) - { - g_snprintf(result->peername, sizeof(result->peername), "%s", "unknown"); - result->t = t; - result->auth_retry_count = g_cfg->sec.login_retry; - } - - return result; -} - -/** - * Deletes a sesman_con struct, freeing resources - * - * After this call, the passed-in pointer is invalid and must not be - * referenced. - * - * Any auth_info struct found in the sesman_con is also deallocated. - * - * @param sc struct to de-allocate - */ -static void -delete_connection(struct sesman_con *sc) -{ - if (sc != NULL) - { - trans_delete(sc->t); - if (sc->auth_info != NULL) - { - auth_end(sc->auth_info); - } - g_free(sc->username); - g_free(sc->ip_addr); - g_free(sc); - } -} - -/*****************************************************************************/ -int -sesman_set_connection_peername(struct sesman_con *sc, const char *name) -{ - int rv = 1; - - if (sc != NULL && name != NULL) - { - g_snprintf(sc->peername, sizeof(sc->peername), "%s", name); - rv = 0; - } - - return rv; -} - /*****************************************************************************/ /** * @@ -301,40 +241,25 @@ static int sesman_listen_test(struct config_sesman *cfg) /******************************************************************************/ int -sesman_close_all(unsigned int flags) +sesman_close_all(void) { - int index; - struct sesman_con *sc; - LOG_DEVEL(LOG_LEVEL_TRACE, "sesman_close_all:"); + pre_session_list_cleanup(); + session_list_cleanup(); + + g_delete_wait_obj(g_reload_event); + g_delete_wait_obj(g_sigchld_event); + g_delete_wait_obj(g_term_event); sesman_delete_listening_transport(); - for (index = 0; index < g_con_list->count; index++) - { - sc = (struct sesman_con *) list_get_item(g_con_list, index); - if (sc != NULL && (flags & SCA_CLOSE_AUTH_INFO) == 0) - { - // Prevent delete_connection() closing the auth_info down - sc->auth_info = NULL; - } - delete_connection(sc); - } + return 0; } /******************************************************************************/ -void -sesman_delete_wait_objects(void) -{ - g_delete_wait_obj(g_reload_event); - g_delete_wait_obj(g_sigchld_event); - g_delete_wait_obj(g_term_event); -} - -/******************************************************************************/ -static int -sesman_data_in(struct trans *self) +int +sesman_scp_data_in(struct trans *self) { int rv; int available; @@ -343,8 +268,10 @@ sesman_data_in(struct trans *self) if (rv == 0 && available) { - struct sesman_con *sc = (struct sesman_con *)self->callback_data; - if ((rv = scp_process(sc)) != 0) + struct pre_session_item *psi; + psi = (struct pre_session_item *)self->callback_data; + + if ((rv = scp_process(psi)) != 0) { LOG(LOG_LEVEL_ERROR, "sesman_data_in: scp_process_msg failed"); } @@ -358,30 +285,79 @@ sesman_data_in(struct trans *self) static int sesman_listen_conn_in(struct trans *self, struct trans *new_self) { - struct sesman_con *sc; - if (g_con_list->count >= MAX_SHORT_LIVED_CONNECTIONS) + struct pre_session_item *psi; + if (pre_session_list_get_count() >= MAX_PRE_SESSION_ITEMS) { - LOG(LOG_LEVEL_ERROR, "sesman_data_in: error, too many " + LOG(LOG_LEVEL_ERROR, "sesman_listen_conn_in: error, too many " "connections, rejecting"); trans_delete(new_self); } - else if ((sc = alloc_connection(new_self)) == NULL || - scp_init_trans(new_self) != 0) + else if ((psi = pre_session_list_new()) == NULL) { LOG(LOG_LEVEL_ERROR, "sesman_data_in: No memory to allocate " "new connection"); - delete_connection(sc); + trans_delete(new_self); + } + else if (scp_init_trans(new_self) != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_data_in: Can't init SCP connection"); + trans_delete(new_self); } else { - new_self->callback_data = (void *)sc; - new_self->trans_data_in = sesman_data_in; - list_add_item(g_con_list, (intptr_t) sc); + new_self->callback_data = (void *)psi; + new_self->trans_data_in = sesman_scp_data_in; + psi->client_trans = new_self; } return 0; } +/******************************************************************************/ +int +sesman_eicp_data_in(struct trans *self) +{ + int rv; + int available; + + rv = eicp_msg_in_check_available(self, &available); + + if (rv == 0 && available) + { + struct pre_session_item *psi; + psi = (struct pre_session_item *)self->callback_data; + if ((rv = eicp_process(psi)) != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_eicp_data_in: eicp_process_msg failed"); + } + eicp_msg_in_reset(self); + } + + return rv; +} + +/******************************************************************************/ +int +sesman_ercp_data_in(struct trans *self) +{ + int rv; + int available; + + rv = ercp_msg_in_check_available(self, &available); + + if (rv == 0 && available) + { + struct session_item *si = (struct session_item *)self->callback_data; + if ((rv = ercp_process(si)) != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_ercp_data_in: ercp_process_msg failed"); + } + ercp_msg_in_reset(self); + } + + return rv; +} + /******************************************************************************/ /** * Informs the main loop a termination signal has been received @@ -396,9 +372,21 @@ set_term_event(int sig) } } +/*****************************************************************************/ +/* No-op signal handler. + */ +static void +sig_no_op(int sig) +{ + /* no-op */ +} + /******************************************************************************/ /** - * Informs the main loop a SIGCHLD has been received + * Catch a SIGCHLD and ignore the main loop + * + * In theory we could use waitpid() in the signal handler, but that + * would prevent us adding any logging */ static void set_sigchld_event(int sig) @@ -438,7 +426,7 @@ sesman_delete_listening_transport(void) } g_list_trans = NULL; - unlock_uds(g_list_trans_lock); + unlock_uds(g_list_trans_lock); // Won't unlock anything for a child process g_list_trans_lock = NULL; } @@ -489,6 +477,13 @@ sesman_create_listening_transport(const struct config_sesman *cfg) return rv; } +/******************************************************************************/ +int +sesman_is_term(void) +{ + return g_is_wait_obj_set(g_term_event); +} + /******************************************************************************/ /** * @@ -500,12 +495,7 @@ sesman_main_loop(void) { int error; int robjs_count; - int wobjs_count; - int timeout; - int index; - intptr_t robjs[32]; - intptr_t wobjs[32]; - struct sesman_con *scon; + intptr_t robjs[1024]; g_con_list = list_create(); if (g_con_list == NULL) @@ -525,47 +515,41 @@ sesman_main_loop(void) error = 0; while (!error) { - timeout = -1; robjs_count = 0; robjs[robjs_count++] = g_term_event; robjs[robjs_count++] = g_sigchld_event; robjs[robjs_count++] = g_reload_event; - wobjs_count = 0; - for (index = 0; index < g_con_list->count; index++) - { - scon = (struct sesman_con *)list_get_item(g_con_list, index); - if (scon != NULL) - { - error = trans_get_wait_objs_rw(scon->t, - robjs, &robjs_count, - wobjs, &wobjs_count, &timeout); - if (error != 0) - { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_get_wait_objs_rw failed"); - break; - } - } - } - if (error != 0) - { - break; - } + if (g_list_trans != NULL) { /* g_list_trans might be NULL on a reconfigure if sesman * is unable to listen again */ - error = trans_get_wait_objs_rw(g_list_trans, robjs, &robjs_count, - wobjs, &wobjs_count, &timeout); + error = trans_get_wait_objs(g_list_trans, robjs, &robjs_count); if (error != 0) { LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_get_wait_objs_rw failed"); + "trans_get_wait_objs failed"); break; } } - if (g_obj_wait(robjs, robjs_count, wobjs, wobjs_count, timeout) != 0) + error = pre_session_list_get_wait_objs(robjs, &robjs_count); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "pre_session_list_get_wait_objs failed"); + break; + } + + error = session_list_get_wait_objs(robjs, &robjs_count); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "session_list_get_wait_objs failed"); + break; + } + + if (g_obj_wait(robjs, robjs_count, NULL, 0, -1) != 0) { /* should not get here */ LOG(LOG_LEVEL_WARNING, "sesman_main_loop: " @@ -580,10 +564,14 @@ sesman_main_loop(void) break; } - if (g_is_wait_obj_set(g_sigchld_event)) /* A child has exited */ + if (g_is_wait_obj_set(g_sigchld_event)) /* term */ { g_reset_wait_obj(g_sigchld_event); - sig_sesman_session_end(); + // Prevent any zombies from hanging around + while (g_waitchild(NULL) > 0) + { + ; + } } if (g_is_wait_obj_set(g_reload_event)) /* We're asked to reload */ @@ -592,33 +580,6 @@ sesman_main_loop(void) sig_sesman_reload_cfg(); } - index = 0; - while (index < g_con_list->count) - { - int remove_con = 0; - scon = (struct sesman_con *)list_get_item(g_con_list, index); - if (trans_check_wait_objs(scon->t) != 0) - { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_check_wait_objs failed, removing trans"); - remove_con = 1; - } - else if (scon->close_requested) - { - remove_con = 1; - } - - if (remove_con) - { - delete_connection(scon); - list_remove_item(g_con_list, index); - } - else - { - ++index; - } - } - if (g_list_trans != NULL) { error = trans_check_wait_objs(g_list_trans); @@ -629,11 +590,25 @@ sesman_main_loop(void) break; } } + + error = pre_session_list_check_wait_objs(); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "pre_session_list_check_wait_objs failed"); + break; + } + + error = session_list_check_wait_objs(); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "session_list_check_wait_objs failed"); + break; + } } - sesman_close_all(SCA_CLOSE_AUTH_INFO); - list_delete(g_con_list); - return 0; + return error; } /*****************************************************************************/ @@ -642,7 +617,7 @@ print_version(void) { g_writeln("xrdp-sesman %s", PACKAGE_VERSION); g_writeln(" The xrdp session manager"); - g_writeln(" Copyright (C) 2004-2020 Jay Sorg, " + g_writeln(" Copyright (C) 2004-2023 Jay Sorg, " "Neutrino Labs, and all contributors."); g_writeln(" See https://github.com/neutrinolabs/xrdp for more information."); g_writeln("%s", ""); @@ -715,16 +690,14 @@ main(int argc, char **argv) enum logReturns log_error; char text[256]; char pid_file[256]; - char default_sesman_ini[256]; struct sesman_startup_params startup_params = {0}; int errored_argc; int daemon; g_init("xrdp-sesman"); g_snprintf(pid_file, 255, "%s/xrdp-sesman.pid", XRDP_PID_PATH); - g_snprintf(default_sesman_ini, 255, "%s/sesman.ini", XRDP_CFG_PATH); - startup_params.sesman_ini = default_sesman_ini; + startup_params.sesman_ini = DEFAULT_SESMAN_INI; errored_argc = sesman_process_params(argc, argv, &startup_params); if (errored_argc > 0) @@ -922,10 +895,11 @@ main(int argc, char **argv) g_snprintf(text, 255, "xrdp_sesman_%8.8x_reload", g_pid); g_reload_event = g_create_wait_obj(text); - g_signal_hang_up(set_reload_event); /* SIGHUP */ g_signal_user_interrupt(set_term_event); /* SIGINT */ g_signal_terminate(set_term_event); /* SIGTERM */ + g_signal_pipe(sig_no_op); /* SIGPIPE */ g_signal_child_stop(set_sigchld_event); /* SIGCHLD */ + g_signal_hang_up(set_reload_event); /* SIGHUP */ if (daemon) { @@ -967,11 +941,10 @@ main(int argc, char **argv) g_chmod_hex("/tmp/.X11-unix", 0x1777); } - error = session_module_init(); - if (error == 0) + if ((error = pre_session_list_init(MAX_PRE_SESSION_ITEMS)) == 0 && + (error = session_list_init()) == 0) { error = sesman_main_loop(); - session_module_cleanup(); } /* clean up PID file on exit */ @@ -980,12 +953,9 @@ main(int argc, char **argv) g_file_delete(pid_file); } - sesman_delete_wait_objects(); + sesman_close_all(); - if (!daemon) - { - log_end(); - } + log_end(); config_free(g_cfg); g_deinit(); diff --git a/sesman/sesman.h b/sesman/sesman.h index e46fe239..5f7c06d2 100644 --- a/sesman/sesman.h +++ b/sesman/sesman.h @@ -27,33 +27,11 @@ #ifndef SESMAN_H #define SESMAN_H -/** - * Type for managing sesman connections from xrdp (etc) - */ -struct sesman_con -{ - struct trans *t; - char peername[15 + 1]; /* Name of peer, if known, for logging */ - int close_requested; /* Set to close the connection normally */ - unsigned int auth_retry_count; - struct auth_info *auth_info; /* non-NULL for an authenticated connection */ - int uid; /* User */ - char *username; /* Username from UID (at time of logon) */ - char *ip_addr; /* Connecting IP address */ -}; +struct config_sesman; +struct trans; /* Globals */ extern struct config_sesman *g_cfg; -extern unsigned char g_fixedkey[8]; - -/** - * Set the peername of a connection - * - * @param name Name to set - * @result 0 for success - */ -int -sesman_set_connection_peername(struct sesman_con *sc, const char *name); /** * Close all file descriptors used by sesman. @@ -61,24 +39,13 @@ sesman_set_connection_peername(struct sesman_con *sc, const char *name); * This is generally used after forking, to make sure the * file descriptors used by the main process are not disturbed * - * This call will also release all trans and SCP_SESSION objects - * held by sesman - * - * @param flags Set SCA_CLOSE_AUTH_INFO to close any open auth_info - * objects. By default these are not cleared, and should - * only be done so when exiting sesman. + * This call will also :- + * - release all trans objects held by sesman + * - Delete sesman wait objects + * - Call sesman_delete_listening_transport() */ -#define SCA_CLOSE_AUTH_INFO (1<<0) int -sesman_close_all(unsigned int flags); - -/** - * Delete sesman wait objects. - * - * Call after forking so we don't break sesman's wait objects - */ -void -sesman_delete_wait_objects(void); +sesman_close_all(void); /* * Remove the listening transport @@ -96,4 +63,28 @@ sesman_delete_listening_transport(void); int sesman_create_listening_transport(const struct config_sesman *cfg); +/** + * Callback to process incoming SCP data + */ +int +sesman_scp_data_in(struct trans *self); + +/** + * Callback to process incoming EICP data + */ +int +sesman_eicp_data_in(struct trans *self); + +/** + * Callback to process incoming ERCP data + */ +int +sesman_ercp_data_in(struct trans *self); + +/* + * Check for termination + */ +int +sesman_is_term(void); + #endif diff --git a/sesman/session_list.c b/sesman/session_list.c index 25eb8102..36a6c9f9 100644 --- a/sesman/session_list.c +++ b/sesman/session_list.c @@ -33,11 +33,16 @@ #include "config_ac.h" #endif +#ifdef HAVE_SYS_PRCTL_H +#include +#endif + #include "arch.h" #include "session_list.h" +#include "trans.h" -#include "sesman_auth.h" #include "sesman_config.h" +#include "list.h" #include "log.h" #include "os_calls.h" #include "sesman.h" @@ -48,12 +53,12 @@ static struct list *g_session_list = NULL; #define SESSION_IN_USE(si) \ ((si) != NULL && \ - (si)->display >= 0 && \ - (si)->pid > 0) + (si)->sesexec_trans != NULL && \ + (si)->sesexec_trans->status == TRANS_STATUS_UP) /******************************************************************************/ int -session_module_init(void) +session_list_init(void) { int rv = 1; if (g_session_list == NULL) @@ -88,9 +93,9 @@ free_session(struct session_item *si) { if (si != NULL) { - if (si->auth_info != NULL) + if (si->sesexec_trans != NULL) { - auth_end(si->auth_info); + trans_delete(si->sesexec_trans); } g_free(si); } @@ -98,7 +103,7 @@ free_session(struct session_item *si) /******************************************************************************/ void -session_module_cleanup(void) +session_list_cleanup(void) { if (g_session_list != NULL) { @@ -123,13 +128,12 @@ session_list_get_count(void) /******************************************************************************/ struct session_item * -session_new(void) +session_list_new(void) { struct session_item *result = g_new0(struct session_item, 1); if (result != NULL) { - result->pid = -1; - result->display = -1; + result->state = E_SESSION_STARTING; if (!list_add_item(g_session_list, (tintptr)result)) { g_free(result); @@ -401,7 +405,7 @@ session_list_get_bydata(uid_t uid, si, SCP_SESSION_TYPE_TO_STR(si->type), si->uid, si->bpp, - si->width, si->height, + si->start_width, si->start_height, si->start_ip_addr); if (si->type != type) @@ -410,7 +414,7 @@ session_list_get_bydata(uid_t uid, continue; } - if ((policy & SESMAN_CFG_SESS_POLICY_U) && (int)uid != si->uid) + if ((policy & SESMAN_CFG_SESS_POLICY_U) && uid != si->uid) { LOG(LOG_LEVEL_DEBUG, "%s: UID doesn't match for 'U' policy", __func__); @@ -425,7 +429,8 @@ session_list_get_bydata(uid_t uid, } if ((policy & SESMAN_CFG_SESS_POLICY_D) && - (si->width != width || si->height != height)) + (si->start_width != width || + si->start_height != height)) { LOG(LOG_LEVEL_DEBUG, "%s: Dimensions don't match for 'D' policy", __func__); @@ -449,98 +454,9 @@ session_list_get_bydata(uid_t uid, return NULL; } -/******************************************************************************/ -/** - * Convert a UID to a username - * - * @param uid UID - * @param uname pointer to output buffer - * @param uname_len Length of output buffer - * @return 0 for success. - */ -static int -username_from_uid(int uid, char *uname, int uname_len) -{ - char *ustr; - int rv = g_getuser_info_by_uid(uid, &ustr, NULL, NULL, NULL, NULL); - - if (rv == 0) - { - g_snprintf(uname, uname_len, "%s", ustr); - g_free(ustr); - } - else - { - g_snprintf(uname, uname_len, ""); - } - return rv; -} - -/******************************************************************************/ -enum session_kill_status -session_list_kill(int pid) -{ - int i = 0; - enum session_kill_status status = SESMAN_SESSION_KILL_NOTFOUND; - - while (i < g_session_list->count) - { - struct session_item *si; - si = (struct session_item *)list_get_item(g_session_list, i); - if (si->pid == pid) - { - status = SESMAN_SESSION_KILL_OK; - if (pid > 0) - { - char username[256]; - username_from_uid(si->uid, username, sizeof(username)); - - /* Log the deletion */ - if (si->auth_info != NULL) - { - LOG(LOG_LEVEL_INFO, - "Calling auth_end for pid %d from pid %d", - pid, g_getpid()); - } - - LOG(LOG_LEVEL_INFO, - "++ terminated session: UID %d (%s), display :%d.0, " - "session_pid %d, ip %s", - si->uid, username, si->display, - si->pid, si->start_ip_addr); - } - - free_session(si); - } - else - { - ++i; - } - } - - return status; -} - -/******************************************************************************/ -void -session_list_sigkill_all(void) -{ - int i; - - for (i = 0 ; i < g_session_list->count ; ++i) - { - struct session_item *si; - si = (struct session_item *)list_get_item(g_session_list, i); - if (si->pid > 0) - { - g_sigterm(si->pid); - } - } -} - /******************************************************************************/ struct scp_session_info * -session_list_get_byuid(int uid, unsigned int *cnt, unsigned char flags) +session_list_get_byuid(uid_t uid, unsigned int *cnt, unsigned int flags) { int i; struct scp_session_info *sess; @@ -549,11 +465,13 @@ session_list_get_byuid(int uid, unsigned int *cnt, unsigned char flags) count = 0; + LOG(LOG_LEVEL_DEBUG, "searching for session by UID: %d", uid); + 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) && uid == si->uid && (si->status & flags) != 0) + if (SESSION_IN_USE(si) && uid == si->uid) { count++; } @@ -579,13 +497,14 @@ session_list_get_byuid(int uid, unsigned int *cnt, unsigned char flags) { const struct session_item *si; si = (const struct session_item *)list_get_item(g_session_list, i); - if (SESSION_IN_USE(si) && uid == si->uid && (si->status & flags) != 0) + + if (SESSION_IN_USE(si) && uid == si->uid) { - (sess[index]).sid = si->pid; + (sess[index]).sid = si->sesexec_pid; (sess[index]).display = si->display; (sess[index]).type = si->type; - (sess[index]).height = si->height; - (sess[index]).width = si->width; + (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; @@ -621,3 +540,56 @@ free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt) g_free(sesslist); } + +/******************************************************************************/ +int +session_list_get_wait_objs(tbus robjs[], int *robjs_count) +{ + 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)) + { + robjs[(*robjs_count)++] = si->sesexec_trans->sck; + } + } + + return 0; +} + +/******************************************************************************/ +int +session_list_check_wait_objs(void) +{ + int i; + + for (i = 0 ; i < g_session_list->count; ++i) + { + struct session_item *si; + si = (struct session_item *)list_get_item(g_session_list, i); + if (SESSION_IN_USE(si)) + { + if (trans_check_wait_objs(si->sesexec_trans) != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_check_wait_objs: " + "trans_check_wait_objs failed, removing trans"); + si->sesexec_trans->status = TRANS_STATUS_DOWN; + } + } + + if (SESSION_IN_USE(si)) + { + ++i; + } + else + { + free_session(si); + list_remove_item(g_session_list, i); + } + } + + return 0; +} diff --git a/sesman/session_list.h b/sesman/session_list.h index e32178e2..b018afad 100644 --- a/sesman/session_list.h +++ b/sesman/session_list.h @@ -1,7 +1,7 @@ /** * xrdp: A Remote Desktop Protocol server. * - * Copyright (C) Jay Sorg 2004-2013 + * 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. @@ -28,53 +28,44 @@ #ifndef SESSION_LIST_H #define SESSION_LIST_H -#include +#include #include "guid.h" #include "scp_application_types.h" #include "xrdp_constants.h" -struct session_parameters; - -#define SESMAN_SESSION_STATUS_ACTIVE 0x01 -#define SESMAN_SESSION_STATUS_IDLE 0x02 -#define SESMAN_SESSION_STATUS_DISCONNECTED 0x04 -/* future expansion -#define SESMAN_SESSION_STATUS_REMCONTROL 0x08 -*/ -#define SESMAN_SESSION_STATUS_ALL 0xFF - -enum session_kill_status +enum session_state { - SESMAN_SESSION_KILL_OK = 0, - SESMAN_SESSION_KILL_NOTFOUND + /** + * Session definition is little more than a sesexec process. We're + * waiting for more details of the session from sesexec */ + E_SESSION_STARTING, + /** Session is fully active */ + E_SESSION_RUNNING }; -struct scp_session_info; - /** * Object describing a session + * + * Unless otherwide noted, fields are only valid if + * the status is E_SESSION_RUNNING */ struct session_item { - int uid; /* UID of session */ - int pid; /* pid of sesman waiting for wm to end */ + enum session_state state; + struct trans *sesexec_trans; // trans for sesexec process. Always valid. + pid_t sesexec_pid; // pid for sesexec process. Always valid + /** + * May be valid if known when the session is starting, otherwise -1 */ int display; - int width; - int height; - int bpp; - struct auth_info *auth_info; - - /* status info */ - unsigned char status; + uid_t uid; enum scp_session_type type; - - /* time data */ - time_t start_time; - // struct session_date disconnect_time; // Currently unused - // struct session_date idle_time; // Currently unused - char start_ip_addr[MAX_PEER_ADDRSTRLEN]; + unsigned short start_width; + unsigned short start_height; + unsigned char bpp; struct guid guid; + char start_ip_addr[MAX_PEER_ADDRSTRLEN]; + time_t start_time; }; /** @@ -84,13 +75,13 @@ struct session_item * Errors are logged */ int -session_module_init(void); +session_list_init(void); /** * Clean up the module on program exit */ void -session_module_cleanup(void); +session_list_cleanup(void); /** * Returns the number of sessions currently active @@ -100,24 +91,27 @@ unsigned int session_list_get_count(void); /** - * Allocates a new session + * Allocates a new session on the list * - * The PID and display for the allocated session will be -1 and all other - * fields will be blank + * state will be E_SESSION_STARTING. Other data must be filled in by + * the caller as appropriate. * * @return pointer to new session object or NULL for no memory * - * After allocating the session successfully, you must initialise the - * PID and display fields with valid numbers. + * After allocating the session, you must initialise the sesexec_trans field + * with a valid transport. * - * If you allocate a session and want to remove it due to other problems, - * use session_kill_pid(-1); + * The session is removed by session_check_wait_objs() when the transport + * goes down (or wasn't allocated in the first place). */ struct session_item * -session_new(void); +session_list_new(void); /** * Get the next available display + * + * The display isn't reserved until the caller has allocated a new session + * (with session_list_new()) and put the new display in it. */ int session_list_get_available_display(void); @@ -136,35 +130,18 @@ session_list_get_bydata(uid_t uid, unsigned char bpp, const char *ip_addr); -/** - * - * @brief kills a session - * @param pid the pid of the session to be killed - * @return - * - */ -enum session_kill_status -session_list_kill(int pid); - -/** - * - * @brief sends sigkill to all sessions - * @return - * - */ -void -session_list_sigkill_all(void); - /** * @brief retrieves session descriptions - * @param UID the UID for the descriptions + * @param uid the UID for the descriptions + * @param[out] cnt The number of sessions returned + * @param flags Future expansion * @return A block of session descriptions * * Pass the return result to free_session_info_list() after use * */ struct scp_session_info * -session_list_get_byuid(int uid, unsigned int *cnt, unsigned char flags); +session_list_get_byuid(uid_t uid, unsigned int *cnt, unsigned int flags); /** * @@ -175,4 +152,21 @@ session_list_get_byuid(int uid, unsigned int *cnt, unsigned char flags); void free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt); +/** + * @brief Get the wait objs for the session list module + * @param @robjs Objects array to update + * @param robjs_count Elements in robjs (by reference) + * @return 0 for success + */ +int +session_list_get_wait_objs(tbus robjs[], int *robjs_count); + + +/** + * @brief Check the wait objs for the session list module + * @return 0 for success + */ +int +session_list_check_wait_objs(void); + #endif // SESSION_LIST_H diff --git a/sesman/sig.c b/sesman/sig.c index 12d195a8..8cdd508a 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -95,24 +95,3 @@ sig_sesman_reload_cfg(void) LOG(LOG_LEVEL_INFO, "configuration reloaded, log subsystem restarted"); } - -/******************************************************************************/ -void -sig_sesman_session_end(void) -{ - int pid; - - LOG(LOG_LEVEL_DEBUG, "receiving SIGCHLD"); - do - { - pid = g_waitchild(NULL); - - if (pid > 0) - { - LOG(LOG_LEVEL_INFO, "Process %d has exited", pid); - - session_list_kill(pid); - } - } - while (pid > 0); -} diff --git a/sesman/sig.h b/sesman/sig.h index 447dde63..8bc3d441 100644 --- a/sesman/sig.h +++ b/sesman/sig.h @@ -35,12 +35,4 @@ void sig_sesman_reload_cfg(void); -/** - * - * @brief SIGCHLD handling code - * - */ -void -sig_sesman_session_end(void); - #endif