xrdp/sesman/sesexec_control.c

229 lines
6.4 KiB
C
Raw Normal View History

/**
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) 2023 Matt Burt
*
* 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_control.c
* @brief Start/stop session executive process
* @author Matt Burt
*
*/
#if defined(HAVE_CONFIG_H)
#include <config_ac.h>
#endif
#include "sesman_config.h"
#include "eicp.h"
#include "log.h"
#include "os_calls.h"
#include "pre_session_list.h"
#include "string_calls.h"
#include "sesexec_control.h"
#include "sesman.h"
#include "trans.h"
#include "xrdp_sockets.h"
#define SESEXEC_SHORTNAME "xrdp-sesexec"
#define SESEXEC_LONGNAME XRDP_LIBEXEC_PATH "/" SESEXEC_SHORTNAME
// Some platforms using setsid() benefit from sesexec being
// forked again, so the UNIX session can be created cleanly
// See FreeBSD bug
// ports/157282: effective login name is not set by xrdp-sesman
// https://www.freebsd.org/cgi/query-pr.cgi?pr=157282
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
#define USE_BSD_SETLOGIN 1
#endif
/*****************************************************************************/
/**
* Adds entries to the args list for running sesexec
*
* @param args Argument list
* @return 0 for memory allocation failure, 1 for success
*/
static int
create_exec_args_add_entries(struct list *args)
{
if (!list_add_strdup(args, SESEXEC_SHORTNAME))
{
return 0;
}
if (g_strcmp(g_cfg->sesman_ini, DEFAULT_SESMAN_INI) != 0)
{
if (!list_add_strdup_multi(args, "-c", g_cfg->sesman_ini, NULL))
{
return 0;
}
}
return 1;
}
/*****************************************************************************/
/**
* Create an args list for sesexec
* @return NULL if memory could not be allocated
*
* The result must be freed with list_delete() after use
*/
static struct list *
create_exec_args(void)
{
struct list *result = list_create();
if (result != NULL)
{
result->auto_free = 1;
if (!create_exec_args_add_entries(result))
{
list_delete(result);
result = NULL;
}
}
return result;
}
/*****************************************************************************/
int
sesexec_start(struct pre_session_item *psi)
{
// Local socket pair used to set up the EICP channel for sesexec
// We also use the socket pair to communicate the PID of sesexec back
// to sesman. Technically we only need this if USE_BSD_SETLOGIN
// is set, but removing this variable complicates the code so
// much it isn't worth it.
int sck[2] = {-1, -1};
int rv = -1;
int size;
const char *exe = SESEXEC_LONGNAME;
struct list *args = NULL;
if (!g_executable_exist(exe))
{
LOG(LOG_LEVEL_ERROR, "Can't execute %s", exe);
}
else if ((args = create_exec_args()) == NULL)
{
LOG(LOG_LEVEL_ERROR, "Out of memory running sesexec");
}
else if (g_sck_local_socketpair(sck) < 0)
{
LOG(LOG_LEVEL_ERROR, "Can't create sesexec EICP socket [%s]",
g_get_strerror());
}
else
{
int pid = g_fork();
if (pid == -1)
{
// Error already logged
g_file_close(sck[0]);
g_file_close(sck[1]);
}
else if (pid == 0)
{
/* Sesexec process */
g_file_close(sck[0]);
#if USE_BSD_SETLOGIN
if (g_fork() != 0)
{
g_exit(0);
}
#endif
// Send our pid back to sesman
pid = g_getpid();
size = g_file_write(sck[1], (const char *)&pid, sizeof(pid));
if (size != sizeof(pid))
{
LOG(LOG_LEVEL_ERROR, "Can't write to PID socket [%s]",
g_get_strerror());
}
else
{
/* Put the number of the file descriptor in EICP_FD
* in the environment */
char buff[64];
g_snprintf(buff, sizeof(buff), "%d", sck[1]);
g_setenv("EICP_FD", buff, 1);
/* [Development] Log all file descriptors not marked cloexec
* other than stdin, stdout, stderr, and the EICP fd in sck[1].
*/
if (sck[1] < 3)
{
// EICP fd has overwritten one of the standard descriptors
LOG_DEVEL_LEAKING_FDS("xrdp-sesexec", 3, -1);
}
else
{
LOG_DEVEL_LEAKING_FDS("xrdp-sesexec", 3, sck[1]);
LOG_DEVEL_LEAKING_FDS("xrdp-sesexec", sck[1] + 1, -1);
}
g_execvp_list(exe, args);
// Shouldn't get here. Errors are logged if we do.
}
g_exit(1);
}
else
{
g_file_close(sck[1]);
// Get the PID from the child (or the grancdchild)
int size = g_file_read(sck[0], (char *)&pid, sizeof(pid));
if (size != sizeof(pid))
{
LOG(LOG_LEVEL_ERROR, "Can't read PID of sesexec process [%s]",
g_get_strerror());
g_file_close(sck[0]);
}
else
{
struct trans *t = eicp_init_trans_from_fd(sck[0],
TRANS_TYPE_CLIENT,
sesman_is_term);
if (t == NULL)
{
LOG(LOG_LEVEL_ERROR, "Can't create sesexec transport [%s]",
g_get_strerror());
g_file_close(sck[0]);
}
else
{
t->trans_data_in = sesman_eicp_data_in;
t->callback_data = (void *)psi;
psi->sesexec_trans = t;
psi->sesexec_pid = pid;
rv = 0;
}
}
}
}
list_delete(args);
return rv;
}