Add CCP support to sesexec
sesexec can now tell the xrdp process to exit, and is aware when the xrdp process exits.
This commit is contained in:
@@ -21,6 +21,8 @@ xrdp_sesexec_SOURCES = \
|
||||
sesexec.h \
|
||||
session.c \
|
||||
session.h \
|
||||
ccp_server.c \
|
||||
ccp_server.h \
|
||||
eicp_server.c \
|
||||
eicp_server.h \
|
||||
ercp_server.c \
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Jay Sorg 2004-2023
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @file eicp_server.c
|
||||
* @brief eicp (executive initialisation control protocol) server function
|
||||
* @author Matt Burt
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(HAVE_CONFIG_H)
|
||||
#include <config_ac.h>
|
||||
#endif
|
||||
|
||||
#include "trans.h"
|
||||
|
||||
#include "ccp.h"
|
||||
#include "ccp_server.h"
|
||||
|
||||
/******************************************************************************/
|
||||
int
|
||||
ccp_server(struct trans *self)
|
||||
{
|
||||
int rv = 0;
|
||||
enum ccp_msg_code msgno;
|
||||
|
||||
switch ((msgno = ccp_msg_in_get_msgno(self)))
|
||||
{
|
||||
default:
|
||||
{
|
||||
char buff[64];
|
||||
ccp_msgno_to_str(msgno, buff, sizeof(buff));
|
||||
LOG(LOG_LEVEL_ERROR, "Ignored CCP message %s", buff);
|
||||
}
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* xrdp: A Remote Desktop Protocol server.
|
||||
*
|
||||
* Copyright (C) Jay Sorg 2004-2023
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @file ccp_server.h
|
||||
* @brief ccp (connection control protocol) server function
|
||||
* @author Matt Burt
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef CCP_SERVER_H
|
||||
#define CCP_SERVER_H
|
||||
|
||||
/**
|
||||
*
|
||||
* @brief Processes an CCP message
|
||||
* @param self The CCP transport the message is coming in on
|
||||
*
|
||||
*/
|
||||
int
|
||||
ccp_server(struct trans *self);
|
||||
|
||||
#endif // CCP_SERVER_H
|
||||
@@ -87,15 +87,33 @@ handle_connect_session_request(struct trans *self)
|
||||
}
|
||||
else
|
||||
{
|
||||
scp_fd = -1; // Don't close this twice!
|
||||
// Ownership of the file descriptor is passed to scp_trans;
|
||||
// don't delete it separately.
|
||||
scp_fd = -1;
|
||||
|
||||
// Now we've got a transport we can send data back to
|
||||
// the SCP client
|
||||
enum scp_sconnect_status scp_status;
|
||||
int display_fd = -1;
|
||||
int chan_fd = -1;
|
||||
|
||||
// Terminate any existing xrdp process to sesexec
|
||||
if (g_ccp_trans != NULL)
|
||||
{
|
||||
sesexec_terminate_connected_xrdp_process(
|
||||
CCP_CLOSE_DISCONNECTED_BY_OTHERCONNECTION);
|
||||
g_sleep(500);
|
||||
}
|
||||
scp_status = get_session_fds(g_session_data, scp_flags,
|
||||
&display_fd, &chan_fd);
|
||||
|
||||
// Tell sesman about the new client connection
|
||||
if (g_ecp_trans != NULL)
|
||||
{
|
||||
/// TODO: client connect event
|
||||
}
|
||||
|
||||
// Pass the session file descriptors to the client
|
||||
rv = scp_send_connect_session_response(scp_trans, scp_status,
|
||||
display_fd, chan_fd);
|
||||
|
||||
@@ -120,10 +138,17 @@ handle_connect_session_request(struct trans *self)
|
||||
session_run_reconnect_script(g_login_info,
|
||||
g_session_data);
|
||||
}
|
||||
|
||||
// Convert the SCP transport to a CCP transport, and
|
||||
// record it
|
||||
if (sesexec_set_ccp_trans(scp_trans) == 0)
|
||||
{
|
||||
scp_trans = NULL; // Prevent transport being deleted.
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of the result of the send, we must close all
|
||||
// our copies of file descriptors.
|
||||
// Close all our copies of file descriptors, including the
|
||||
// SCP transport if we failed to convert it to a CCP transport
|
||||
if (display_fd >= 0)
|
||||
{
|
||||
g_file_close(display_fd);
|
||||
@@ -132,7 +157,10 @@ handle_connect_session_request(struct trans *self)
|
||||
{
|
||||
g_file_close(chan_fd);
|
||||
}
|
||||
trans_delete(scp_trans);
|
||||
if (scp_trans != NULL)
|
||||
{
|
||||
trans_delete(scp_trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+193
-5
@@ -32,6 +32,8 @@
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "arch.h"
|
||||
#include "ccp.h"
|
||||
#include "ccp_server.h"
|
||||
#include "eicp.h"
|
||||
#include "eicp_server.h"
|
||||
#include "ercp.h"
|
||||
@@ -52,6 +54,12 @@ struct startup_params
|
||||
const char *sesman_ini;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MAX_ROBJS = 32, ///< Maximum number of file objects in use at any one time
|
||||
XRDP_EXIT_TIMEOUT = 2500 ///< Time to wait for xrdp process to exit
|
||||
};
|
||||
|
||||
/*
|
||||
* Program-scope globals
|
||||
*/
|
||||
@@ -64,10 +72,12 @@ tintptr g_term_event = 0;
|
||||
tintptr g_sigchld_event = 0;
|
||||
pid_t g_pid;
|
||||
|
||||
struct trans *g_ecp_trans;
|
||||
struct trans *g_ccp_trans;
|
||||
|
||||
/*
|
||||
* Module-scope globals
|
||||
*/
|
||||
static struct trans *g_ecp_trans;
|
||||
static pid_t g_ecp_pid;
|
||||
static int g_terminate_loop = 0;
|
||||
static int g_terminate_status = 0;
|
||||
@@ -163,6 +173,27 @@ sesexec_ercp_data_in(struct trans *self)
|
||||
return rv;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
static int
|
||||
sesexec_ccp_data_in(struct trans *self)
|
||||
{
|
||||
int rv;
|
||||
int available;
|
||||
|
||||
rv = ccp_msg_in_check_available(self, &available);
|
||||
|
||||
if (rv == 0 && available)
|
||||
{
|
||||
if ((rv = ccp_server(self)) != 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "%s: ccp_server failed", __func__);
|
||||
}
|
||||
ccp_msg_in_reset(self);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/**
|
||||
* Informs the main loop a termination signal has been received
|
||||
@@ -235,6 +266,12 @@ sesexec_set_ecp_transport(struct trans *t)
|
||||
g_ecp_pid = 0;
|
||||
rv = 0;
|
||||
}
|
||||
else if (t == g_ecp_trans)
|
||||
{
|
||||
// This would break the memory subsystem!
|
||||
LOG(LOG_LEVEL_ERROR, "%s: programming error", __func__);
|
||||
rv = 1;
|
||||
}
|
||||
else if ((rv = g_sck_get_peer_cred(t->sck, &pid, &uid, &gid)) != 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "Can't get credentials of sesman socket [%s]",
|
||||
@@ -286,6 +323,19 @@ sesexec_main_loop_cleanup(void)
|
||||
session_data_free(g_session_data);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/**
|
||||
* Close the CCP trans unconditionally
|
||||
*
|
||||
* Use this call if you are certain the other end has gone away
|
||||
*/
|
||||
static void
|
||||
close_ccp_trans(void)
|
||||
{
|
||||
trans_delete(g_ccp_trans);
|
||||
g_ccp_trans = NULL;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/**
|
||||
*
|
||||
@@ -297,7 +347,6 @@ sesexec_main_loop(void)
|
||||
{
|
||||
int error = 0;
|
||||
int robjs_count;
|
||||
#define MAX_ROBJS 32
|
||||
intptr_t robjs[MAX_ROBJS];
|
||||
|
||||
g_terminate_loop = 0;
|
||||
@@ -323,6 +372,19 @@ sesexec_main_loop(void)
|
||||
}
|
||||
}
|
||||
|
||||
// CCP transport is set if we have an xrdp connection
|
||||
if (g_ccp_trans != NULL)
|
||||
{
|
||||
error = trans_get_wait_objs(g_ccp_trans, robjs, &robjs_count);
|
||||
if (error != 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: "
|
||||
"trans_get_wait_objs(CCP) failed");
|
||||
sesexec_terminate_main_loop(error);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Add any objects from the discover module
|
||||
error = sesexec_discover_get_wait_objs(robjs, &robjs_count, MAX_ROBJS);
|
||||
if (error != 0)
|
||||
@@ -370,13 +432,16 @@ sesexec_main_loop(void)
|
||||
session_process_sigchld_event(g_session_data);
|
||||
if (session_was_active && !session_active(g_session_data))
|
||||
{
|
||||
// We've finished the session. Tell sesman and
|
||||
// We've finished the session. Tell sesman, xrdp and
|
||||
// finish up.
|
||||
if (g_ecp_trans != NULL)
|
||||
{
|
||||
(void)ercp_send_session_finished_event(g_ecp_trans);
|
||||
}
|
||||
|
||||
sesexec_terminate_connected_xrdp_process(
|
||||
CCP_CLOSE_LOGOFF_BY_USER);
|
||||
|
||||
session_data_free(g_session_data);
|
||||
g_session_data = NULL;
|
||||
sesexec_terminate_main_loop(0);
|
||||
@@ -411,6 +476,33 @@ sesexec_main_loop(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (g_ccp_trans != NULL)
|
||||
{
|
||||
error = trans_check_wait_objs(g_ccp_trans);
|
||||
if (error != 0)
|
||||
{
|
||||
if (g_ccp_trans->status != TRANS_STATUS_UP)
|
||||
{
|
||||
// xrdp has gone away.
|
||||
LOG(LOG_LEVEL_INFO, "sesexec_main_loop: "
|
||||
"xrdp has exited");
|
||||
// TODO: Tell sesman xrdp has exited
|
||||
close_ccp_trans();
|
||||
}
|
||||
else
|
||||
{
|
||||
// A callback has failed. This shouldn't really happen.
|
||||
// Try to signal a software failure to the xrdp process
|
||||
LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: "
|
||||
"trans_check_wait_objs failed for CCP transport");
|
||||
sesexec_terminate_connected_xrdp_process(
|
||||
CCP_CLOSE_SOFTWARE_FAILURE);
|
||||
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
error = sesexec_discover_check_wait_objs();
|
||||
if (error != 0)
|
||||
{
|
||||
@@ -419,14 +511,16 @@ sesexec_main_loop(void)
|
||||
sesexec_terminate_main_loop(error);
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* close sesman communications immediately */
|
||||
sesexec_set_ecp_transport(NULL);
|
||||
|
||||
/* We should already have notified xrdp of the reason why we are
|
||||
* closing, in which case this call has no effect */
|
||||
sesexec_terminate_connected_xrdp_process(CCP_CLOSE_SOFTWARE_FAILURE);
|
||||
|
||||
return g_terminate_status;
|
||||
#undef MAX_ROBJS
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -598,3 +692,97 @@ main(int argc, char **argv)
|
||||
g_deinit();
|
||||
return error;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void
|
||||
sesexec_terminate_connected_xrdp_process(enum ccp_close_reason_type reason)
|
||||
{
|
||||
if (g_ccp_trans != NULL && g_ccp_trans->status == TRANS_STATUS_UP)
|
||||
{
|
||||
// Ask xrdp to exit, specifying the reason to return to
|
||||
// the RDP client (if possible)
|
||||
(void)ccp_send_close_connection_request(g_ccp_trans, reason);
|
||||
|
||||
unsigned int start_ms = g_get_elapsed_ms();
|
||||
while (1)
|
||||
{
|
||||
int robjs_count = 0;
|
||||
intptr_t robjs[MAX_ROBJS];
|
||||
|
||||
// How long have we been waiting for xrdp to exit?
|
||||
unsigned int elapsed = g_get_elapsed_ms() - start_ms;
|
||||
if (elapsed > XRDP_EXIT_TIMEOUT)
|
||||
{
|
||||
// A timeout has occurred
|
||||
LOG(LOG_LEVEL_WARNING,
|
||||
"xrdp process failed to exit after %u ms", elapsed);
|
||||
break;
|
||||
}
|
||||
|
||||
robjs[robjs_count++] = g_term_event;
|
||||
if (trans_get_wait_objs(g_ccp_trans, robjs, &robjs_count) != 0)
|
||||
{
|
||||
// Transport has gone away
|
||||
LOG(LOG_LEVEL_WARNING,
|
||||
"xrdp process exited after %u ms", elapsed);
|
||||
break;
|
||||
}
|
||||
if (g_obj_wait(robjs, robjs_count, NULL, 0,
|
||||
XRDP_EXIT_TIMEOUT - elapsed) != 0)
|
||||
{
|
||||
/* should not get here */
|
||||
g_sleep(100);
|
||||
}
|
||||
else if (g_is_wait_obj_set(g_term_event))
|
||||
{
|
||||
// Get out as quickly as possible
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This will cause trans_get_wait_objs() to return a failure
|
||||
// when the end of the data on the socket is reached.
|
||||
(void)trans_check_wait_objs(g_ccp_trans);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close_ccp_trans();
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int
|
||||
sesexec_set_ccp_trans(struct trans *scp_trans)
|
||||
{
|
||||
int rv = 1;
|
||||
pid_t pid;
|
||||
|
||||
if (scp_trans == NULL)
|
||||
{
|
||||
close_ccp_trans();
|
||||
rv = 0;
|
||||
}
|
||||
else if (scp_trans == g_ccp_trans)
|
||||
{
|
||||
// This would break the memory subsystem!
|
||||
LOG(LOG_LEVEL_ERROR, "%s: programming error", __func__);
|
||||
rv = 1;
|
||||
}
|
||||
else if ((rv = g_sck_get_peer_cred(scp_trans->sck, &pid, NULL, NULL)) != 0)
|
||||
{
|
||||
LOG(LOG_LEVEL_ERROR, "Can't get credentials of xrdp socket [%s]",
|
||||
g_get_strerror());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Convert the transport to a CCP transport
|
||||
ccp_trans_from_scp_trans(scp_trans,
|
||||
sesexec_ccp_data_in,
|
||||
NULL);
|
||||
trans_delete(g_ccp_trans);
|
||||
g_ccp_trans = scp_trans;
|
||||
rv = 0;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "ccp_application_types.h"
|
||||
|
||||
struct config_sesman;
|
||||
struct trans;
|
||||
struct login_info;
|
||||
@@ -39,15 +41,70 @@ struct session_data;
|
||||
#endif
|
||||
|
||||
/* Globals */
|
||||
/**
|
||||
* Pointer to config data for sesman/sesexec
|
||||
*/
|
||||
extern struct config_sesman *g_cfg;
|
||||
|
||||
/**
|
||||
* DES key used to obfuscate VNC password files.
|
||||
*
|
||||
* This key is not documented in RFC6143, but can readily be found by
|
||||
* searching VNC sources.
|
||||
*
|
||||
* You can also find the 'reversed' form "e84ad660c4721ae0"
|
||||
* on the net, particulary for openssl one-liners to decrypt VNC
|
||||
* password files.
|
||||
*/
|
||||
extern unsigned char g_fixedkey[8];
|
||||
|
||||
/**
|
||||
* Information about the logged-in user
|
||||
*
|
||||
* This is set when the user successfully passes authentication
|
||||
*/
|
||||
extern struct login_info *g_login_info;
|
||||
|
||||
/**
|
||||
* Information about the user session (opaque type)
|
||||
*
|
||||
* Set when the user is succesfully logged in
|
||||
*/
|
||||
extern struct session_data *g_session_data;
|
||||
|
||||
/**
|
||||
* Program has received a termination event
|
||||
*/
|
||||
extern tintptr g_term_event;
|
||||
|
||||
/**
|
||||
* Program has received one or more SIGCHLD events
|
||||
*/
|
||||
extern tintptr g_sigchld_event;
|
||||
|
||||
/**
|
||||
* PID of sesexec process
|
||||
*
|
||||
* Used to detect when we are running in a form of the original process
|
||||
*/
|
||||
extern pid_t g_pid;
|
||||
|
||||
/**
|
||||
* EICP/ERCP transport
|
||||
*
|
||||
* Used to communicate with the sesman process
|
||||
*
|
||||
* Use sesexec_is_ecp_active() if you need to check sesman is
|
||||
* truly there.
|
||||
*/
|
||||
extern struct trans *g_ecp_trans;
|
||||
|
||||
/**
|
||||
* CCP transport
|
||||
*
|
||||
* Used to communicate with the currently connected xrdp process
|
||||
*/
|
||||
extern struct trans *g_ccp_trans;
|
||||
|
||||
/**
|
||||
* Callback to process incoming ERCP data
|
||||
@@ -55,14 +112,18 @@ extern pid_t g_pid;
|
||||
int
|
||||
sesexec_ercp_data_in(struct trans *self);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Check for termination
|
||||
*
|
||||
* @return boolean. Set if program has been asked to terminate
|
||||
*/
|
||||
int
|
||||
sesexec_is_term(void);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Terminate the sesexec main loop
|
||||
*
|
||||
* @param status Status to return to the OS
|
||||
*/
|
||||
void
|
||||
sesexec_terminate_main_loop(int status);
|
||||
@@ -79,12 +140,35 @@ sesexec_terminate_main_loop(int status);
|
||||
int
|
||||
sesexec_set_ecp_transport(struct trans *t);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Is the ECP transport still active?
|
||||
*
|
||||
* @result boolean
|
||||
*
|
||||
* This is intended to be used as a guard to prevent an active ECP
|
||||
* transport being overwritten. Do not use it to check if sesman is
|
||||
* active before sending messages, as this introduces a race condition.
|
||||
*/
|
||||
int
|
||||
sesexec_is_ecp_active(void);
|
||||
|
||||
/**
|
||||
* Terminate an active xrdp process
|
||||
*
|
||||
* @param Reason to pass back to the xrdp process (if connected)
|
||||
*
|
||||
* After this call, g_ccp_trans will be NULL
|
||||
*/
|
||||
void
|
||||
sesexec_terminate_connected_xrdp_process(enum ccp_close_reason_type reason);
|
||||
|
||||
/**
|
||||
* Set the CCP transport from an SCP transport
|
||||
*
|
||||
* This call is intended to be used at the end of a connection
|
||||
* event, to record our end of the connection to the xrdp process
|
||||
*/
|
||||
int
|
||||
sesexec_set_ccp_trans(struct trans *scp_trans);
|
||||
|
||||
#endif // SESEXEC_H
|
||||
|
||||
Reference in New Issue
Block a user