New files for sesexec

This commit is contained in:
matt335672
2023-03-23 12:50:40 +00:00
parent ae94891ab7
commit 8853b1c4ee
9 changed files with 1429 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
AM_CPPFLAGS = \
-DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \
-DXRDP_SBIN_PATH=\"${sbindir}\" \
-DXRDP_LIBEXEC_PATH=\"${libexecdir}/xrdp\" \
-DXRDP_SOCKET_PATH=\"${socketdir}\" \
-I$(top_srcdir)/sesman/libsesman \
-I$(top_srcdir)/libipm \
-I$(top_srcdir)/common
SESEXEC_EXTRA_LIBS =
pkglibexec_PROGRAMS = \
xrdp-sesexec
xrdp_sesexec_SOURCES = \
sesexec.c \
sesexec.h \
session.c \
session.h \
eicp_server.c \
eicp_server.h \
ercp_server.c \
ercp_server.h \
env.c \
env.h \
login_info.c \
login_info.h \
xauth.c \
xauth.h \
xwait.c \
xwait.h
xrdp_sesexec_LDFLAGS =
xrdp_sesexec_LDADD = \
$(top_builddir)/sesman/libsesman/libsesman.la \
$(top_builddir)/libipm/libipm.la \
$(top_builddir)/common/libcommon.la \
$(SESEXEC_EXTRA_LIBS)
+201
View File
@@ -0,0 +1,201 @@
/**
* 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 "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
{
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_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)
{
int scp_fd;
struct session_parameters sp = {0};
int rv;
rv = eicp_get_create_session_request(self, &scp_fd, &sp.display,
&sp.type, &sp.width, &sp.height,
&sp.bpp, &sp.shell, &sp.directory);
if (rv == 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)
{
LOG(LOG_LEVEL_ERROR, "Can't create SCP trans");
g_file_close(scp_fd);
rv = 1;
}
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);
}
// Return the status to the SCP client
rv = scp_send_create_session_response(scp_trans, scp_status,
sp.display, &sp.guid);
trans_delete(scp_trans);
// Further comms from sesexec 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)
{
rv = 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));
}
else
{
rv = ercp_send_session_finished_event(self);
sesexec_terminate_main_loop(1);
}
}
}
return rv;
}
/******************************************************************************/
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_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;
}
+39
View File
@@ -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 eicp_server.h
* @brief eicp (executive initialisation control protocol) server function
* @author Matt Burt
*
*/
#ifndef EICP_SERVER_H
#define EICP_SERVER_H
/**
*
* @brief Processes an EICP message
* @param self The EICP transport the message is coming in on
*
*/
int
eicp_server(struct trans *self);
#endif // EICP_SERVER_H
+69
View File
@@ -0,0 +1,69 @@
/**
* 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 ercp_server.c
* @brief ercp (executive run-time control protocol) server function
* @author Matt Burt
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "arch.h"
#include "sesexec.h"
#include "session.h"
#include "trans.h"
#include "ercp.h"
#include "ercp_server.h"
/******************************************************************************/
static int
handle_session_reconnect_event(struct trans *self)
{
session_reconnect(g_login_info, g_session_data);
return 0;
}
/******************************************************************************/
int
ercp_server(struct trans *self)
{
int rv = 0;
enum ercp_msg_code msgno;
switch ((msgno = ercp_msg_in_get_msgno(self)))
{
case E_ERCP_SESSION_RECONNECT_EVENT:
rv = handle_session_reconnect_event(self);
break;
default:
{
char buff[64];
ercp_msgno_to_str(msgno, buff, sizeof(buff));
LOG(LOG_LEVEL_ERROR, "Ignored ERCP message %s", buff);
}
}
return rv;
}
+39
View File
@@ -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 ercp_server.h
* @brief ercp (executive run-time control protocol) server function
* @author Matt Burt
*
*/
#ifndef ERCP_SERVER_H
#define ERCP_SERVER_H
/**
*
* @brief Processes an ERCP message
* @param self The ERCP transport the message is coming in on
*
*/
int
ercp_server(struct trans *self);
#endif // ERCP_SERVER_H
+357
View File
@@ -0,0 +1,357 @@
/**
* 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 login_info.c
* @brief Define functionality associated with user logins for sesexec
* @author Matt Burt
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "login_info.h"
#include "trans.h"
#include "sesman_auth.h"
#include "sesman_access.h"
#include "sesman_config.h"
#include "login_info.h"
#include "os_calls.h"
#include "scp.h"
#include "sesexec.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());
}
/******************************************************************************/
/**
* Authenticate and authorize the connection
*
* @param username Name for user
* @param password Password
* @param ip_addr Remote IP address
* @param login_info Structure to fill in for a successful login
* @return Status for the operation
*
* @post If E_SCP_LOGIN_OK is returned, g_login_info is filled in
*
*/
static enum scp_login_status
authenticate_and_authorize_connection(const char *supplied_username,
const char *password,
const char *ip_addr,
struct login_info *login_info)
{
int uid;
char *username; // From reverse-looking up the UID
enum scp_login_status status;
struct auth_info *auth_info;
if (g_getuser_info_by_name(supplied_username,
&uid, NULL, NULL, NULL, NULL) != 0)
{
/* we can't get a UID for the user */
LOG(LOG_LEVEL_ERROR, "Can't get UID for user %s",
supplied_username);
log_authfail_message(supplied_username, ip_addr);
status = E_SCP_LOGIN_NOT_AUTHENTICATED;
}
else if (g_getuser_info_by_uid(uid,
&username,
NULL, NULL, NULL, NULL) != 0)
{
LOG(LOG_LEVEL_ERROR, "Can't reverse lookup UID %d", uid);
status = E_SCP_LOGIN_NOT_AUTHENTICATED;
}
else
{
if (g_strcmp(username, supplied_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
*/
LOG(LOG_LEVEL_WARNING,
"Using username %s for the session (from UID %d)",
username, uid);
}
auth_info = auth_userpass(username, password, ip_addr, &status);
/* Sanity check on result of call */
if ((auth_info != NULL && status != E_SCP_LOGIN_OK) ||
(auth_info == NULL && status == E_SCP_LOGIN_OK))
{
LOG(LOG_LEVEL_ERROR, "Bugcheck; inconsistent auth result. "
"info = %p, status = %d", (void *)auth_info, (int)status);
status = E_SCP_LOGIN_GENERAL_ERROR;
auth_end(auth_info);
auth_info = NULL;
}
/* Group access allowed? */
if (status == E_SCP_LOGIN_OK &&
!access_login_allowed(&g_cfg->sec, username))
{
LOG(LOG_LEVEL_INFO, "Username okay but group problem for "
"user: %s", username);
status = E_SCP_LOGIN_NOT_AUTHORIZED;
auth_end(auth_info);
auth_info = NULL;
}
switch (status)
{
case E_SCP_LOGIN_OK:
{
char *dup_username = g_strdup(username);
char *dup_ip_addr = 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;
auth_end(auth_info);
auth_info = NULL;
}
else
{
LOG(LOG_LEVEL_INFO, "Access permitted for user: %s",
username);
login_info->uid = uid;
login_info->username = dup_username;
login_info->ip_addr = dup_ip_addr;
login_info->auth_info = auth_info;
}
}
break;
case E_SCP_LOGIN_NOT_AUTHENTICATED:
log_authfail_message(username, ip_addr);
break;
default:
break;
}
g_free(username);
}
return status;
}
/******************************************************************************/
static int
get_scp_client_retry(struct trans *scp_trans,
const char **username, const char **password,
const char **ip_addr)
{
int got_message = 0;
// Wait for an SCP message
enum scp_msg_code msgno;
scp_msg_in_reset(scp_trans);
if (scp_msg_in_wait_available(scp_trans) == 0)
{
msgno = scp_msg_in_get_msgno(scp_trans);
switch (msgno)
{
case E_SCP_SYS_LOGIN_REQUEST:
if (scp_get_sys_login_request(scp_trans, username,
password, ip_addr) == 0)
{
got_message = 1;
}
break;
case E_SCP_CLOSE_CONNECTION_REQUEST:
break;
default:
{
char buff[64];
scp_msgno_to_str(msgno, buff, sizeof(buff));
LOG(LOG_LEVEL_ERROR, "unexpected message %s from SCP client",
buff);
}
break;
}
}
return got_message;
}
/******************************************************************************/
struct login_info *
login_info_sys_login_user(struct trans *scp_trans,
const char *username,
const char *password,
const char *ip_addr)
{
struct login_info *result;
enum scp_login_status status = E_SCP_LOGIN_GENERAL_ERROR;
int server_closed = 0;
if ((result = g_new0(struct login_info, 1)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "Allocation failure logging in user");
}
else
{
int first_time = 1;
unsigned int retry_count = g_cfg->sec.login_retry;
result->uid = (uid_t) -1;
while (status != E_SCP_LOGIN_OK && !server_closed)
{
// First time round, we have credentials supplied by the
// caller. On subsequent trips, we have to wait for the
// SCP client to send us more.
if (first_time)
{
first_time = 0;
}
else if (!get_scp_client_retry(scp_trans, &username,
&password, &ip_addr))
{
status = E_SCP_LOGIN_GENERAL_ERROR;
break;
}
status = authenticate_and_authorize_connection(username,
password,
ip_addr,
result);
if (status != E_SCP_LOGIN_OK)
{
if (retry_count > 0)
{
--retry_count;
}
else
{
server_closed = 1;
}
}
if (scp_send_login_response(scp_trans, status, server_closed) != 0)
{
status = E_SCP_LOGIN_GENERAL_ERROR;
break;
}
}
}
if (status != E_SCP_LOGIN_OK)
{
login_info_free(result);
result = NULL;
}
return result;
}
/******************************************************************************/
struct login_info *
login_info_uds_login_user(struct trans *scp_trans)
{
struct login_info *result;
int uid; // Needed as g_sck_get_peer_cred() doesn't use uid_t
// Allocate a struct for the result, with the IP address set to ""
if ((result = g_new0(struct login_info, 1)) == NULL ||
(result->ip_addr = g_new0(char, 1)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "Allocation failure logging in user");
}
else if (g_sck_get_peer_cred(scp_trans->sck, NULL, &uid, NULL) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to get peer credentials for SCP socket");
}
else if (g_getuser_info_by_uid(uid, &result->username,
NULL, NULL, NULL, NULL) != 0)
{
LOG(LOG_LEVEL_ERROR, "Can't reverse lookup UID %d", result->uid);
}
else if ((result->auth_info = auth_uds(result->username, NULL)) == NULL)
{
LOG(LOG_LEVEL_ERROR, "Can't authorize user %s over UDS",
result->username);
}
else if (!access_login_allowed(&g_cfg->sec, result->username))
{
LOG(LOG_LEVEL_ERROR, "Access denied for user %s by your system admin",
result->username);
}
else
{
result->uid = (uid_t)uid;
return result;
}
login_info_free(result);
return NULL;
}
/******************************************************************************/
void
login_info_free(struct login_info *self)
{
if (self != NULL)
{
g_free(self->username);
g_free(self->ip_addr);
if (self->auth_info != NULL)
{
auth_end(self->auth_info);
}
g_free(self);
}
}
+94
View File
@@ -0,0 +1,94 @@
/**
* 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 login_info.h
* @brief Declare functionality associated with user logins for sesexec
* @author Matt Burt
*
*/
#ifndef LOGIN_INFO_H
#define LOGIN_INFO_H
#include <sys/types.h>
struct trans;
/**
* Information associated with the logged-in user
*/
struct login_info
{
uid_t uid;
char *username;
char *ip_addr;
struct auth_info *auth_info;
};
/**
* @brief Attempt a system login using username/password
* @param scp_trans SCP transport for talking to the client
* @param username Username from xrdp
* @param password Password from xrdp
* @param ip_addr IP address for xrdp client
*
* @result Allocated login_info struct for a successful login
*
* This is a wrapper around the dialogue between sesexec and the SCP process
* which is required to start a session.
*
* While this call is in operation, only the scp_trans transport will
* be checked for messages. Incoming messages on other transports will be
* ignored. The dialog can also be terminated by a SIGTERM if the SCP
* transport is configured to allow this.
*
* The username in the returned structure may differ from the passed-in
* username if multiple names map to the same UID. This can happen with
* federated naming services (e.g. AD, LDAP)
*/
struct login_info *
login_info_sys_login_user(struct trans *scp_trans,
const char *username,
const char *password,
const char *ip_addr);
/**
* @brief Create a login_info structure using UDS credentials
*
* This should be a formality, as by the time sesexec tries this, sesman
* should already have done it.
*
* Errors are logged.
*
* @param scp_trans SCP transport for talking to the client
* @param ip_addr IP address for xrdp client
*
* @result Allocated login_info struct for a successful login
*/
struct login_info *
login_info_uds_login_user(struct trans *scp_trans);
/**
* Free a struct login_info
*/
void
login_info_free(struct login_info *self);
#endif // LOGIN_INFO_H
+521
View File
@@ -0,0 +1,521 @@
/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Matt Burt 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 sesexec.c
* @brief Main program file for session executive process
* @author Matt Burt
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include <ctype.h>
#include <stdarg.h>
#include "arch.h"
#include "eicp.h"
#include "eicp_server.h"
#include "ercp.h"
#include "ercp_server.h"
#include "login_info.h"
#include "sesexec.h"
#include "sesman_config.h"
#include "log.h"
#include "os_calls.h"
#include "session.h"
#include "string_calls.h"
#include "trans.h"
#include "xrdp_sockets.h"
struct startup_params
{
const char *sesman_ini;
};
/*
* Program-scope globals
*/
struct config_sesman *g_cfg;
unsigned char g_fixedkey[8] = { 23, 82, 107, 6, 35, 78, 88, 7 };
struct login_info *g_login_info;
struct session_data *g_session_data;
tintptr g_term_event = 0;
tintptr g_sigchld_event = 0;
pid_t g_pid;
/*
* Module-scope globals
*/
static struct trans *g_ecp_trans;
static int g_terminate_loop = 0;
static int g_terminate_status = 0;
/*****************************************************************************/
/**
* Command line argument parser
* @param[in] argc number of command line arguments
* @param[in] argv pointer array of commandline arguments
* @param[out] startup_params Returned startup parameters
* @return 0 on success
*/
static int
process_params(int argc, char **argv,
struct startup_params *startup_params)
{
int index;
const char *option;
const char *value;
startup_params->sesman_ini = DEFAULT_SESMAN_INI;
index = 1;
while (index < argc)
{
option = argv[index];
if (index + 1 < argc)
{
value = argv[index + 1];
}
else
{
value = "";
}
if (g_strcmp(option, "-c") == 0)
{
index++;
startup_params->sesman_ini = value;
}
else /* unknown option */
{
return index;
}
index++;
}
return 0;
}
/******************************************************************************/
#if 0
static int
sesexec_scp_data_in(struct trans *self)
{
int rv;
int available;
rv = scp_msg_in_check_available(self, &available);
if (rv == 0 && available)
{
struct sesman_con *sc = (struct sesman_con *)self->callback_data;
//if ((rv = scp_process(sc)) != 0)
{
LOG(LOG_LEVEL_ERROR, "%s: scp_process failed", __func__);
}
scp_msg_in_reset(self);
}
return rv;
}
#endif
/******************************************************************************/
static int
sesexec_eicp_data_in(struct trans *self)
{
int rv;
int available;
rv = eicp_msg_in_check_available(self, &available);
if (rv == 0 && available)
{
if ((rv = eicp_server(self)) != 0)
{
LOG(LOG_LEVEL_ERROR, "%s: eicp_server failed", __func__);
}
eicp_msg_in_reset(self);
}
return rv;
}
/******************************************************************************/
int
sesexec_ercp_data_in(struct trans *self)
{
int rv;
int available;
rv = ercp_msg_in_check_available(self, &available);
if (rv == 0 && available)
{
if ((rv = ercp_server(self)) != 0)
{
LOG(LOG_LEVEL_ERROR, "%s: ercp_server failed", __func__);
}
ercp_msg_in_reset(self);
}
return rv;
}
/******************************************************************************/
/**
* Informs the main loop a termination signal has been received
*/
static void
set_term_event(int sig)
{
/* Don't try to use a wait obj in a child process */
if (g_getpid() == g_pid)
{
g_set_wait_obj(g_term_event);
}
}
/*****************************************************************************/
/* No-op signal handler.
*/
static void
sig_no_op(int sig)
{
/* no-op */
}
/******************************************************************************/
/**
* Informs the main loop a child exiting signal has been received
*/
static void
set_sigchld_event(int sig)
{
/* Don't try to use a wait obj in a child process */
if (g_getpid() == g_pid)
{
g_set_wait_obj(g_sigchld_event);
}
}
/******************************************************************************/
int
sesexec_is_term(void)
{
return g_terminate_loop || g_is_wait_obj_set(g_term_event);
}
/******************************************************************************/
void
sesexec_terminate_main_loop(int status)
{
g_terminate_loop = 1;
g_terminate_status = status;
}
/******************************************************************************/
static void
process_sigchld_event(void)
{
struct exit_status e;
int pid;
// Check for any finished children
while ((pid = g_waitchild(&e)) > 0)
{
session_process_child_exit(g_session_data, pid, &e);
}
}
/******************************************************************************/
/**
*
* @brief Starts sesexec main loop
*
*/
static int
sesexec_main_loop(void)
{
int error = 0;
int robjs_count;
intptr_t robjs[32];
g_terminate_loop = 0;
g_terminate_status = 0;
g_login_info = NULL;
while (!g_terminate_loop)
{
robjs_count = 0;
robjs[robjs_count++] = g_term_event;
robjs[robjs_count++] = g_sigchld_event;
error = trans_get_wait_objs(g_ecp_trans, robjs, &robjs_count);
if (error != 0)
{
LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: "
"trans_get_wait_objs(ECP) failed");
sesexec_terminate_main_loop(error);
continue;
}
if (g_obj_wait(robjs, robjs_count, NULL, 0, 0) != 0)
{
/* should not get here */
LOG(LOG_LEVEL_WARNING, "sesexec_main_loop: "
"Unexpected error from g_obj_wait()");
g_sleep(100);
continue;
}
if (g_is_wait_obj_set(g_term_event)) /* term */
{
g_reset_wait_obj(g_term_event);
if (session_active(g_session_data))
{
// Ask the active session to terminate
LOG(LOG_LEVEL_INFO, "sesexec_main_loop: "
"sesexec asked to terminate. "
"Terminating active session");
session_send_term(g_session_data);
}
else
{
// Terminate immediately
LOG(LOG_LEVEL_INFO, "sesexec_main_loop: "
"sesexec asked to terminate. "
"No session is active");
sesexec_terminate_main_loop(0);
continue;
}
}
if (g_is_wait_obj_set(g_sigchld_event)) /* SIGCHLD */
{
g_reset_wait_obj(g_sigchld_event);
// See whether the session goes from active to inactive
// after processing SIGCHLD
int session_was_active = session_active(g_session_data);
process_sigchld_event();
if (session_was_active && !session_active(g_session_data))
{
// We've finished the session. Tell sesman and
// finish up.
(void)ercp_send_session_finished_event(g_ecp_trans);
session_data_free(g_session_data);
g_session_data = NULL;
sesexec_terminate_main_loop(0);
continue;
}
}
error = trans_check_wait_objs(g_ecp_trans);
if (error != 0)
{
LOG(LOG_LEVEL_ERROR, "sesexec_main_loop: "
"trans_check_wait_objs failed for ECP transport");
sesexec_terminate_main_loop(error);
continue;
}
}
login_info_free(g_login_info);
return g_terminate_status;
}
/******************************************************************************/
static int start_logging(const char *sesman_ini)
{
char text[256];
int rv = 1;
if (!g_file_exist(sesman_ini))
{
g_printf("Config file %s does not exist\n", sesman_ini);
}
else
{
enum logReturns log_error;
log_error = log_start(sesman_ini, "xrdp-sesexec", 0);
if (log_error != LOG_STARTUP_OK)
{
switch (log_error)
{
case LOG_ERROR_MALLOC:
g_writeln("error on malloc. cannot start logging. quitting.");
break;
case LOG_ERROR_FILE_OPEN:
g_writeln("error opening log file [%s]. quitting.",
getLogFile(text, sizeof(text) - 1));
break;
default:
// Assume sufficient messages have already been generated
break;
}
}
else
{
rv = 0;
}
}
return rv;
}
/******************************************************************************/
static int
get_eicp_fd(char errstr[], unsigned int errstr_size)
{
const char *s = g_getenv("EICP_FD");
const char *p;
int fd;
errstr[0] = '\0';
if (s == NULL || s[0] == '\0')
{
g_snprintf(errstr, errstr_size,
"Can't read EICP_FD environment variable");
return -1;
}
for (p = s ; isdigit(*p) ; ++p)
{
;
}
if (*p != '\0')
{
g_snprintf(errstr, errstr_size, "EICP_FD has non-digit char '%c'", *p);
return -1;
}
if ((p - s) > 4)
{
g_snprintf(errstr, errstr_size, "EICP_FD has too many digits");
return -1;
}
fd = g_atoi(s);
if (!g_file_is_open(fd))
{
g_snprintf(errstr, errstr_size, "EICP_FD %d is not open", fd);
return -1;
}
return fd;
}
/******************************************************************************/
int
main(int argc, char **argv)
{
int error = 1;
struct startup_params startup_params = {0};
int errored_argc;
int eicp_fd;
char eicp_errstr[128];
/*
* Check the EICP transport file descriptor is provided and open
* before opening any log files, config files, etc. We then open
* log files, and log errors at that point */
eicp_fd = get_eicp_fd(eicp_errstr, sizeof(eicp_errstr));
g_init("xrdp-sesexec");
//g_sleep(15 * 1000);
errored_argc = process_params(argc, argv, &startup_params);
if (errored_argc > 0)
{
g_writeln("Unknown option: %s", argv[errored_argc]);
}
/* starting logging subsystem
*
* For historic reasons, we share a log file with sesman */
else if (start_logging(startup_params.sesman_ini) == 0)
{
/* reading config
*
* For historic reasons, we share a config with sesman */
if ((g_cfg = config_read(startup_params.sesman_ini)) == NULL)
{
LOG(LOG_LEVEL_ALWAYS, "error reading config %s: %s",
startup_params.sesman_ini, g_get_strerror());
}
else if (eicp_fd < 0)
{
LOG(LOG_LEVEL_ERROR, "%s", eicp_errstr);
}
else
{
char text[128];
g_pid = g_getpid();
/* signal handling */
g_snprintf(text, sizeof(text), "xrdp_sesexec_%8.8x_main_term",
g_pid);
g_term_event = g_create_wait_obj(text);
g_snprintf(text, sizeof(text), "xrdp_sesexec_%8.8x_sigchld",
g_pid);
g_sigchld_event = g_create_wait_obj(text);
// No need to terminate on SIGINT for sesexec. This can
// also make it hard to debug sessions.
//g_signal_user_interrupt(set_term_event);
g_signal_terminate(set_term_event); /* SIGTERM */
g_signal_pipe(sig_no_op); /* SIGPIPE */
g_signal_child_stop(set_sigchld_event);
/* Set up an EICP process handler
* Errors are logged by this call if necessary */
g_ecp_trans = eicp_init_trans_from_fd(eicp_fd,
TRANS_TYPE_SERVER,
sesexec_is_term);
if (g_ecp_trans != NULL)
{
g_ecp_trans->trans_data_in = sesexec_eicp_data_in;
g_ecp_trans->callback_data = NULL;
/* start program main loop */
LOG(LOG_LEVEL_INFO, "starting xrdp-sesexec with pid %d", g_pid);
error = sesexec_main_loop();
trans_delete(g_ecp_trans);
}
g_delete_wait_obj(g_term_event);
}
config_free(g_cfg);
log_end();
}
g_deinit();
g_exit(error);
}
+70
View File
@@ -0,0 +1,70 @@
/**
* 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 sesexec.h
* @brief Main include file
* @author Jay Sorg
*
*/
#ifndef SESEXEC_H
#define SESEXEC_H
#include <sys/types.h>
struct config_sesman;
struct trans;
struct login_info;
struct session_data;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define USE_BSD_SETLOGIN
#endif
/* Globals */
extern struct config_sesman *g_cfg;
extern unsigned char g_fixedkey[8];
extern struct login_info *g_login_info;
extern struct session_data *g_session_data;
extern tintptr g_term_event;
extern tintptr g_sigchld_event;
extern pid_t g_pid;
/**
* Callback to process incoming ERCP data
*/
int
sesexec_ercp_data_in(struct trans *self);
/*
* Check for termination
*/
int
sesexec_is_term(void);
/*
* Terminate the sesexec main loop
*/
void
sesexec_terminate_main_loop(int status);
#endif // SESEXEC_H