3e38d9be80
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.
259 lines
7.9 KiB
C
259 lines
7.9 KiB
C
/**
|
|
* xrdp: A Remote Desktop Protocol server.
|
|
*
|
|
* Copyright (C) Jay Sorg 2004-2023
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
*
|
|
* @file eicp_server.c
|
|
* @brief eicp (executive initialisation control protocol) server function
|
|
* @author Matt Burt
|
|
*
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include <config_ac.h>
|
|
#endif
|
|
|
|
#include "trans.h"
|
|
|
|
#include "eicp.h"
|
|
#include "eicp_server.h"
|
|
#include "login_info.h"
|
|
#include "os_calls.h"
|
|
#include "ercp.h"
|
|
#include "scp.h"
|
|
#include "sesexec.h"
|
|
#include "sesexec_discover.h"
|
|
#include "session.h"
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
handle_sys_login_request(struct trans *self)
|
|
{
|
|
const char *username;
|
|
const char *password;
|
|
const char *ip_addr;
|
|
int scp_fd;
|
|
|
|
int rv = eicp_get_sys_login_request(self, &username,
|
|
&password, &ip_addr, &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 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);
|
|
|
|
if (g_login_info != NULL)
|
|
{
|
|
rv = eicp_send_sys_login_response(self, 1,
|
|
g_login_info->uid, scp_fd);
|
|
}
|
|
else
|
|
{
|
|
rv = eicp_send_sys_login_response(self, 0, (uid_t) -1, 0);
|
|
}
|
|
|
|
trans_delete(scp_trans); // Closes scp_fd as well
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
LOG(LOG_LEVEL_INFO, "xrdp-sesexec pid %d is now logging out", g_pid);
|
|
sesexec_terminate_main_loop(0);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
handle_create_session_request(struct trans *self)
|
|
{
|
|
struct session_parameters sp = {0};
|
|
int status;
|
|
|
|
status = eicp_get_create_session_request(
|
|
self, &sp.display,
|
|
&sp.type, &sp.width, &sp.height,
|
|
&sp.bpp, &sp.shell, &sp.directory);
|
|
if (status == 0)
|
|
{
|
|
enum scp_screate_status scp_status = E_SCP_SCREATE_OK;
|
|
|
|
// Must be logged in to start a session
|
|
if (g_login_info == NULL)
|
|
{
|
|
scp_status = E_SCP_SCREATE_NOT_LOGGED_IN;
|
|
}
|
|
else
|
|
{
|
|
// 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);
|
|
|
|
// 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
|
|
// asking us to start a session, and our reply. This
|
|
// could be many seconds, and a new sesman may well
|
|
// have started.
|
|
// If we enable the restart functionality at
|
|
// this point, we have a race condition that could
|
|
// result in a session which sesman doesn't know
|
|
// about. The simplest thing to do in this rare situation
|
|
// is to abort the session - the user can create a
|
|
// new one
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"sesman appears to have failed - stopping session");
|
|
}
|
|
else if ((status = sesexec_discover_enable()) != 0)
|
|
{
|
|
// Equally regrettable - we can't make the session
|
|
// discoverable, so we'll stop it.
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"unable to make session discoverable"
|
|
" - stopping session");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status != 0)
|
|
{
|
|
// Kill sesexec, and any active session
|
|
sesexec_terminate_main_loop(status);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
eicp_server(struct trans *self)
|
|
{
|
|
int rv = 0;
|
|
enum eicp_msg_code msgno;
|
|
|
|
switch ((msgno = eicp_msg_in_get_msgno(self)))
|
|
{
|
|
case E_EICP_SYS_LOGIN_REQUEST:
|
|
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;
|
|
|
|
case E_EICP_CREATE_SESSION_REQUEST:
|
|
rv = handle_create_session_request(self);
|
|
break;
|
|
|
|
default:
|
|
{
|
|
char buff[64];
|
|
eicp_msgno_to_str(msgno, buff, sizeof(buff));
|
|
LOG(LOG_LEVEL_ERROR, "Ignored EICP message %s", buff);
|
|
}
|
|
}
|
|
return rv;
|
|
}
|