Files
xrdp/sesman/session_list.c
T
matt335672 3e38d9be80 Rework the start session method in SCP
The start session method is reworked to pass a response back to the
client.
If the method was successful, a session list entry is created. This
is different to the provious behaviour, where we created the
session list entry unconditionally.

This new arrangement means that we need a different way to avoid
a race condition where two users may try to create a session at the
same time, and end up with the same display. We do this by keeping
track of newly allocated displays as part of the SCP connections. When
we allocate a display, the SCP connection displays are also taken
into account.
2025-07-07 20:09:20 +01:00

432 lines
11 KiB
C

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg 2004-2015
*
* BSD process grouping by:
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland.
* Copyright (c) 2000-2001 Markus Friedl.
* Copyright (c) 2011-2015 Koichiro Iwao, Kyushu Institute of Technology.
*
* 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 session_list.c
* @brief Session list management code
* @author Jay Sorg, Simone Fedele
*
*/
#if defined(HAVE_CONFIG_H)
#include "config_ac.h"
#endif
#include "arch.h"
#include "session_list.h"
#include "trans.h"
#include "sesman_config.h"
#include "list.h"
#include "log.h"
#include "os_calls.h"
#include "sesman.h"
#include "set_int.h"
#include "string_calls.h"
#include "xrdp_sockets.h"
static struct list *g_session_list = NULL;
#define SESSION_IN_USE(si) \
((si) != NULL && \
(si)->sesexec_trans != NULL && \
(si)->sesexec_trans->status == TRANS_STATUS_UP)
/******************************************************************************/
int
session_list_init(void)
{
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");
rv = 1;
}
else
{
g_session_list->auto_free = 0;
}
}
return rv;
}
/******************************************************************************/
/**
* Frees resources allocated to a session_item
*
* @param si Session item
*
* @note Any pointer to this item on g_session_list will be invalid
* after this call.
*/
static void
free_session(struct session_item *si)
{
if (si != NULL)
{
if (si->sesexec_trans != NULL)
{
trans_delete(si->sesexec_trans);
}
g_free(si);
}
}
/******************************************************************************/
void
session_list_cleanup(void)
{
if (g_session_list != NULL)
{
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);
free_session(si);
}
list_delete(g_session_list);
g_session_list = NULL;
}
}
/******************************************************************************/
unsigned int
session_list_get_count(void)
{
return (g_session_list == NULL) ? 0 : g_session_list->count;
}
/******************************************************************************/
unsigned int
session_list_get_count_by_state(enum session_state state)
{
unsigned int result = 0;
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->state == state)
{
++result;
}
}
return result;
}
/******************************************************************************/
struct session_item *
session_list_new(void)
{
struct session_item *result = g_new0(struct session_item, 1);
if (result != NULL)
{
result->state = E_SESSION_STARTING;
if (!list_add_item(g_session_list, (tintptr)result))
{
g_free(result);
result = NULL;
}
}
return result;
}
/******************************************************************************/
void
session_list_get_session_displays(struct set_int *alloc_displays)
{
int count = (g_session_list == NULL) ? 0 : g_session_list->count;
int i = 0;
for (i = 0 ; i < count ; ++i)
{
struct session_item *si;
si = (struct session_item *)list_get_item(g_session_list, i);
if (SESSION_IN_USE(si))
{
set_int_add(alloc_displays, si->display);
}
}
}
/******************************************************************************/
struct session_item *
session_list_get_bydata(uid_t uid,
enum scp_session_type type,
unsigned short width,
unsigned short height,
unsigned char bpp,
const char *ip_addr)
{
char policy_str[64];
int policy = g_cfg->sess.policy;
int i;
if (ip_addr == NULL)
{
ip_addr = "";
}
if ((policy & SESMAN_CFG_SESS_POLICY_DEFAULT) != 0)
{
/* Before xrdp v0.9.14, the default
* session policy varied by type. If this is needed again
* in the future, here is the place to add it */
policy = SESMAN_CFG_SESS_POLICY_U | SESMAN_CFG_SESS_POLICY_B;
}
config_output_policy_string(policy, policy_str, sizeof(policy_str));
LOG(LOG_LEVEL_DEBUG,
"%s: search policy=%s type=%s U=%d B=%d D=(%dx%d) I=%s",
__func__,
policy_str, SCP_SESSION_TYPE_TO_STR(type),
uid, bpp, width, height,
ip_addr);
/* 'Separate' policy never matches */
if (policy & SESMAN_CFG_SESS_POLICY_SEPARATE)
{
LOG(LOG_LEVEL_DEBUG, "%s: No matches possible", __func__);
return NULL;
}
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))
{
continue;
}
LOG(LOG_LEVEL_DEBUG,
"%s: try %p type=%s U=%d B=%d D=(%dx%d) I=%s",
__func__,
si,
SCP_SESSION_TYPE_TO_STR(si->type),
si->uid, si->bpp,
si->start_width, si->start_height,
si->start_ip_addr);
if (si->type != type)
{
LOG(LOG_LEVEL_DEBUG, "%s: Type doesn't match", __func__);
continue;
}
if ((policy & SESMAN_CFG_SESS_POLICY_U) && uid != si->uid)
{
LOG(LOG_LEVEL_DEBUG,
"%s: UID doesn't match for 'U' policy", __func__);
continue;
}
if ((policy & SESMAN_CFG_SESS_POLICY_B) && si->bpp != bpp)
{
LOG(LOG_LEVEL_DEBUG,
"%s: bpp doesn't match for 'B' policy", __func__);
continue;
}
if ((policy & SESMAN_CFG_SESS_POLICY_D) &&
(si->start_width != width ||
si->start_height != height))
{
LOG(LOG_LEVEL_DEBUG,
"%s: Dimensions don't match for 'D' policy", __func__);
continue;
}
if ((policy & SESMAN_CFG_SESS_POLICY_I) &&
g_strcmp(si->start_ip_addr, ip_addr) != 0)
{
LOG(LOG_LEVEL_DEBUG,
"%s: IPs don't match for 'I' policy", __func__);
continue;
}
LOG(LOG_LEVEL_DEBUG,
"%s: Got match, display=%d", __func__, si->display);
return si;
}
LOG(LOG_LEVEL_DEBUG, "%s: No matches found", __func__);
return NULL;
}
/******************************************************************************/
struct scp_session_info *
session_list_get_byuid(const uid_t *uid, unsigned int *cnt, unsigned int flags)
{
int i;
struct scp_session_info *sess;
int count;
int index;
count = 0;
if (uid != NULL)
{
LOG(LOG_LEVEL_DEBUG, "searching for session by UID: %d", (int)*uid);
}
else
{
LOG(LOG_LEVEL_DEBUG, "searching for all sessions");
}
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 == NULL || *uid == si->uid))
{
count++;
}
}
if (count == 0)
{
(*cnt) = 0;
return 0;
}
/* malloc() an array of disconnected sessions */
sess = g_new0(struct scp_session_info, count);
if (sess == 0)
{
(*cnt) = 0;
return 0;
}
index = 0;
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 == NULL || *uid == si->uid))
{
(sess[index]).sid = si->sesexec_pid;
(sess[index]).display = si->display;
(sess[index]).type = si->type;
(sess[index]).height = si->start_height;
(sess[index]).width = si->start_width;
(sess[index]).bpp = si->bpp;
(sess[index]).start_time = si->start_time;
(sess[index]).uid = si->uid;
(sess[index]).start_ip_addr = g_strdup(si->start_ip_addr);
/* Check for string allocation failures */
if ((sess[index]).start_ip_addr == NULL)
{
free_session_info_list(sess, *cnt);
(*cnt) = 0;
return 0;
}
index++;
}
}
(*cnt) = count;
return sess;
}
/******************************************************************************/
void
free_session_info_list(struct scp_session_info *sesslist, unsigned int cnt)
{
if (sesslist != NULL && cnt > 0)
{
unsigned int i;
for (i = 0 ; i < cnt ; ++i)
{
g_free(sesslist[i].start_ip_addr);
}
}
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 = 0;
while (i < g_session_list->count)
{
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;
}