1525 lines
46 KiB
C
1525 lines
46 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.c
|
|
* @brief Session management code
|
|
* @author Jay Sorg, Simone Fedele
|
|
*
|
|
*/
|
|
|
|
#if defined(HAVE_CONFIG_H)
|
|
#include "config_ac.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include "arch.h"
|
|
#include "session.h"
|
|
|
|
#include "sesman_auth.h"
|
|
#include "sesman_config.h"
|
|
#include "env.h"
|
|
#include "guid.h"
|
|
#include "list.h"
|
|
#include "log.h"
|
|
#include "login_info.h"
|
|
#include "os_calls.h"
|
|
#include "sesexec.h"
|
|
#include "sessionrecord.h"
|
|
#include "ssl_calls.h"
|
|
#include "string_calls.h"
|
|
#include "trans.h"
|
|
#include "xauth.h"
|
|
#include "xwait.h"
|
|
#include "xrdp_sockets.h"
|
|
|
|
struct session_data
|
|
{
|
|
pid_t x_server; ///< PID of X server
|
|
pid_t win_mgr; ///< PID of window manager
|
|
pid_t chansrv; ///< PID of chansrv
|
|
time_t start_time;
|
|
unsigned int connect_count;
|
|
char display[MAX_DISPLAY_NAME_SIZE]; // Set by session_start()
|
|
struct session_parameters params;
|
|
// Flexible array member used to store strings in params and ip_addr;
|
|
#ifdef __cplusplus
|
|
char strings[1];
|
|
#else
|
|
char strings[];
|
|
#endif
|
|
};
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Create a new session_data structure from a session_parameters object
|
|
*
|
|
* @param sp Session parameters passed to session_start()
|
|
* @return semi-initialised session_data struct
|
|
*/
|
|
static struct session_data *
|
|
session_data_new(const struct session_parameters *sp)
|
|
{
|
|
unsigned int string_length = 0;
|
|
// What string length do we need?
|
|
string_length += g_strlen(sp->shell) + 1;
|
|
string_length += g_strlen(sp->directory) + 1;
|
|
string_length += g_strlen(sp->instance_name) + 1;
|
|
|
|
struct session_data *sd = (struct session_data *)g_malloc(sizeof(*sd) + string_length, 0);
|
|
|
|
if (sd == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Out of memory allocating session data struct");
|
|
}
|
|
else
|
|
{
|
|
sd->win_mgr = -1;
|
|
sd->x_server = -1;
|
|
sd->chansrv = -1;
|
|
sd->start_time = 0;
|
|
sd->connect_count = 0;
|
|
|
|
/* Copy all the non-string session parameters... */
|
|
sd->params = *sp;
|
|
|
|
/* ...and then the strings */
|
|
char *memptr = sd->strings;
|
|
|
|
#define COPY_STRING(dest,src) \
|
|
(dest) = memptr; \
|
|
strcpy(memptr, src); \
|
|
memptr += strlen(memptr) + 1
|
|
|
|
COPY_STRING(sd->params.shell, sp->shell);
|
|
COPY_STRING(sd->params.directory, sp->directory);
|
|
COPY_STRING(sd->params.instance_name, sp->instance_name);
|
|
|
|
#undef COPY_STRING
|
|
}
|
|
|
|
return sd;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_data_free(struct session_data *session_data)
|
|
{
|
|
if (session_data != NULL)
|
|
{
|
|
#ifdef USE_DEVEL_LOGGING
|
|
if (session_data->win_mgr > 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING,
|
|
"Freeing session data with valid window manager PID %d",
|
|
session_data->win_mgr);
|
|
}
|
|
if (session_data->x_server > 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING,
|
|
"Freeing session data with valid X server PID %d",
|
|
session_data->x_server);
|
|
}
|
|
if (session_data->chansrv > 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_WARNING,
|
|
"Freeing session data with valid chansrv PID %d",
|
|
session_data->chansrv);
|
|
}
|
|
#endif
|
|
|
|
free(session_data);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Creates a string consisting of all parameters that is hosted in the param list
|
|
* @param outstr allocate this buffer before you use this function
|
|
* @param len the allocated len for outstr
|
|
*/
|
|
static char *
|
|
dumpItemsToString(struct list *self, char *outstr, int len)
|
|
{
|
|
int index;
|
|
int totalLen = 0;
|
|
|
|
g_memset(outstr, 0, len);
|
|
if (self->count == 0)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_TRACE, "List is empty");
|
|
}
|
|
|
|
for (index = 0; index < self->count; index++)
|
|
{
|
|
/* +1 = one space*/
|
|
totalLen = totalLen + g_strlen((char *)list_get_item(self, index)) + 1;
|
|
|
|
if (len > totalLen)
|
|
{
|
|
g_strcat(outstr, (char *)list_get_item(self, index));
|
|
g_strcat(outstr, " ");
|
|
}
|
|
}
|
|
|
|
return outstr ;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static void
|
|
start_chansrv(const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
void *closure /* unused */)
|
|
{
|
|
struct list *chansrv_params = list_create();
|
|
const char *exe_path = XRDP_SBIN_PATH "/xrdp-chansrv";
|
|
|
|
if (chansrv_params != NULL)
|
|
{
|
|
chansrv_params->auto_free = 1;
|
|
if (!list_add_strdup(chansrv_params, exe_path))
|
|
{
|
|
list_delete(chansrv_params);
|
|
chansrv_params = NULL;
|
|
}
|
|
}
|
|
|
|
if (chansrv_params == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Out of memory starting chansrv");
|
|
}
|
|
else
|
|
{
|
|
env_set_user(login_info->uid,
|
|
g_cfg->env_names,
|
|
g_cfg->env_values);
|
|
|
|
LOG_DEVEL_LEAKING_FDS("chansrv", 3, -1);
|
|
|
|
/* executing chansrv */
|
|
g_execvp_list(exe_path, chansrv_params);
|
|
|
|
/* should not get here */
|
|
list_delete(chansrv_params);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static void
|
|
start_window_manager(const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
void *closure /* unused */)
|
|
{
|
|
char text[256];
|
|
const struct session_parameters *sp = &sd->params;
|
|
|
|
env_set_user(login_info->uid,
|
|
g_cfg->env_names,
|
|
g_cfg->env_values);
|
|
|
|
auth_set_env(login_info->auth_info);
|
|
LOG_DEVEL_LEAKING_FDS("window manager", 3, -1);
|
|
|
|
if (sp->directory[0] != '\0')
|
|
{
|
|
if (g_cfg->sec.allow_alternate_shell)
|
|
{
|
|
g_set_current_dir(sp->directory);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Directory change to %s requested, but not "
|
|
"allowed by AllowAlternateShell config value.",
|
|
sp->directory);
|
|
}
|
|
}
|
|
|
|
if (sp->shell[0] != '\0')
|
|
{
|
|
if (g_cfg->sec.allow_alternate_shell)
|
|
{
|
|
if (g_cfg->sec.pass_shell_as_env != NULL &&
|
|
g_cfg->sec.pass_shell_as_env[0] != '\0')
|
|
{
|
|
// Pass the shell in to the standard startwm scripts
|
|
// in an environment variable
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Setting variable '%s' to the specified shell of '%s'",
|
|
g_cfg->sec.pass_shell_as_env,
|
|
sp->shell);
|
|
g_setenv_log(g_cfg->sec.pass_shell_as_env, sp->shell, 1);
|
|
}
|
|
else
|
|
{
|
|
// Try to execute the shell directly (if permitted)
|
|
if (g_strchr(sp->shell, ' ') != 0 ||
|
|
g_strchr(sp->shell, '\t') != 0)
|
|
{
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Using user requested window manager on "
|
|
"display %s with embedded arguments using a shell: %s",
|
|
sd->display, sp->shell);
|
|
const char *argv[] = {"sh", "-c", sp->shell, NULL};
|
|
g_execvp("/bin/sh", (char **)argv);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Using user requested window manager on "
|
|
"display %s %s", sd->display, sp->shell);
|
|
g_execlp3(sp->shell, sp->shell, 0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Shell %s requested by user, but not allowed by "
|
|
"AllowAlternateShell config value.",
|
|
sp->shell);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "The user session on display %s did "
|
|
"not request a specific window manager", sd->display);
|
|
}
|
|
|
|
/* try to execute user window manager if enabled */
|
|
if (g_cfg->enable_user_wm)
|
|
{
|
|
g_snprintf(text, sizeof(text), "%s/%s",
|
|
g_getenv("HOME"), g_cfg->user_wm);
|
|
if (g_file_exist(text))
|
|
{
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Using window manager on display %s"
|
|
" from user home directory: %s", sd->display, text);
|
|
g_execlp3(text, g_cfg->user_wm, 0);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG,
|
|
"The user home directory window manager configuration "
|
|
"is enabled but window manager program does not exist: %s",
|
|
text);
|
|
}
|
|
}
|
|
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Using the default window manager on display %s: %s",
|
|
sd->display, g_cfg->default_wm);
|
|
g_execlp3(g_cfg->default_wm, g_cfg->default_wm, 0);
|
|
|
|
/* still a problem starting window manager just start xterm */
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"No window manager on display %s started, "
|
|
"so falling back to starting xterm for user debugging",
|
|
sd->display);
|
|
g_execlp3("xterm", "xterm", 0);
|
|
|
|
/* should not get here */
|
|
LOG(LOG_LEVEL_ERROR, "A fatal error has occurred attempting to start "
|
|
"the window manager on display %s, aborting connection",
|
|
sd->display);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static struct list *
|
|
prepare_xorg_xserver_params(const struct session_data *sd,
|
|
const char *authfile)
|
|
{
|
|
|
|
char screen[32]; /* display number */
|
|
char text[128];
|
|
const char *xserver;
|
|
|
|
struct list *params = list_create();
|
|
if (params != NULL)
|
|
{
|
|
params->auto_free = 1;
|
|
|
|
/*
|
|
* Make sure Xorg doesn't run setuid root. Root access is not
|
|
* needed. Xorg can fail when run as root and the user has no
|
|
* console permissions.
|
|
*/
|
|
if (g_cfg->sec.xorg_no_new_privileges && g_no_new_privs() != 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"[session start] (display :%d): Failed to disable "
|
|
"setuid on X server: %s",
|
|
sd->params.x11_display, g_get_strerror());
|
|
}
|
|
|
|
g_snprintf(screen, sizeof(screen), ":%d", sd->params.x11_display);
|
|
|
|
/* some args are passed via env vars */
|
|
g_snprintf(text, sizeof(text), "%d", sd->params.width);
|
|
g_setenv_log("XRDP_START_WIDTH", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), "%d", sd->params.height);
|
|
g_setenv_log("XRDP_START_HEIGHT", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), "%d", g_cfg->sess.max_idle_time);
|
|
g_setenv_log("XRDP_SESMAN_MAX_IDLE_TIME", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), "%d", g_cfg->sess.max_disc_time);
|
|
g_setenv_log("XRDP_SESMAN_MAX_DISC_TIME", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), "%d", g_cfg->sess.kill_disconnected);
|
|
g_setenv_log("XRDP_SESMAN_KILL_DISCONNECTED", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), XRDP_X11RDP_BASE_STR, sd->display);
|
|
g_setenv_log("XRDP_X11RDP_SOCKET", text, 1);
|
|
|
|
g_snprintf(text, sizeof(text), XRDP_DISCONNECT_BASE_STR, sd->display);
|
|
g_setenv_log("XRDP_DISCONNECT_SOCKET", text, 1);
|
|
|
|
/* get path of Xorg from config */
|
|
xserver = (const char *)list_get_item(g_cfg->xorg_params, 0);
|
|
|
|
/* these are the must have parameters */
|
|
list_add_strdup_multi(params,
|
|
xserver, screen,
|
|
"-auth", authfile,
|
|
LIST_ADD_STRDUP_TERM);
|
|
|
|
/* additional parameters from sesman.ini file */
|
|
list_append_list_strdup(g_cfg->xorg_params, params, 1);
|
|
}
|
|
|
|
return params;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Create an Xvnc password file
|
|
*
|
|
* @param x11_display X11 display number
|
|
* @return Name of passwd file, or NULL if no memory.
|
|
*
|
|
* env_set_user() must be called before calling this function
|
|
*/
|
|
static char *
|
|
get_xvnc_passwd_file_name(int x11_display)
|
|
{
|
|
char *result = NULL;
|
|
int len;
|
|
|
|
char *pw_username = NULL;
|
|
char *pw_dir = NULL;
|
|
char hostname[256];
|
|
int error;
|
|
|
|
/* Get parameters needed for VNC filename */
|
|
hostname[sizeof(hostname) - 1] = '\0';
|
|
g_gethostname(hostname, sizeof(hostname));
|
|
error = g_getuser_info_by_uid(g_getuid(), &pw_username, 0, 0, &pw_dir, 0);
|
|
|
|
if (error != 0 || pw_username == NULL || pw_dir == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't get parameters for XVnc passwd file");
|
|
}
|
|
else
|
|
{
|
|
if (0 == g_cfg->auth_file_path)
|
|
{
|
|
/* if no auth_file_path is set, then we go for
|
|
$HOME/.vnc/sesman_passwd-USERNAME@HOSTNAME:DISPLAY */
|
|
if (!g_directory_exist(".vnc"))
|
|
{
|
|
if (g_mkdir(".vnc") < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Error creating .vnc directory: %s",
|
|
g_get_strerror());
|
|
}
|
|
}
|
|
|
|
len = g_snprintf(NULL, 0, "%s/.vnc/sesman_passwd-%s@%s:%d",
|
|
pw_dir, pw_username, hostname, x11_display);
|
|
++len; // Allow for terminator
|
|
|
|
result = (char *) g_malloc(len, 1);
|
|
if (result != NULL)
|
|
{
|
|
/* Try legacy names first, remove if found */
|
|
g_snprintf(result, len,
|
|
"%s/.vnc/sesman_%s_passwd:%d",
|
|
pw_dir, pw_username, x11_display);
|
|
if (g_file_exist(result))
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Removing old "
|
|
"password file %s", result);
|
|
g_file_delete(result);
|
|
}
|
|
g_snprintf(result, len,
|
|
"%s/.vnc/sesman_%s_passwd",
|
|
pw_dir, pw_username);
|
|
if (g_file_exist(result))
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Removing insecure "
|
|
"password file %s", result);
|
|
g_file_delete(result);
|
|
}
|
|
g_snprintf(result, len,
|
|
"%s/.vnc/sesman_passwd-%s@%s:%d",
|
|
pw_dir, pw_username, hostname, x11_display);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* we use auth_file_path as requested */
|
|
len = g_snprintf(NULL, 0, g_cfg->auth_file_path, pw_username);
|
|
|
|
++len; // Allow for terminator
|
|
result = (char *) g_malloc(len, 1);
|
|
if (result != NULL)
|
|
{
|
|
g_snprintf(result, len,
|
|
g_cfg->auth_file_path, pw_username);
|
|
}
|
|
}
|
|
|
|
if (result == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Can't allocate memory for Xvnc passwd file name");
|
|
}
|
|
else
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_DEBUG, "pass file: %s", result);
|
|
}
|
|
}
|
|
g_free(pw_username);
|
|
g_free(pw_dir);
|
|
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
set_xvnc_passwd(const char *filename, const char *passwd)
|
|
{
|
|
char encryptedPasswd[16];
|
|
char key[24];
|
|
char passwd_hash[20];
|
|
char passwd_hash_text[40];
|
|
int fd;
|
|
int passwd_bytes;
|
|
void *des;
|
|
void *sha1;
|
|
|
|
if (filename == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING, "Cannot write VNC password hash to NULL file");
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* If we're in FIPS mode, do not write the GUID to disk after it's
|
|
* been encrypted with an insecure algorithm.
|
|
*/
|
|
if (g_fips_mode_enabled())
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't create VNC password file in FIPS mode");
|
|
return 1;
|
|
}
|
|
/* create password hash from password */
|
|
passwd_bytes = (passwd == NULL) ? 0 : strlen(passwd);
|
|
sha1 = ssl_sha1_info_create();
|
|
ssl_sha1_clear(sha1);
|
|
ssl_sha1_transform(sha1, "xrdp_vnc", 8);
|
|
ssl_sha1_transform(sha1, passwd, passwd_bytes);
|
|
ssl_sha1_transform(sha1, passwd, passwd_bytes);
|
|
ssl_sha1_complete(sha1, passwd_hash);
|
|
ssl_sha1_info_delete(sha1);
|
|
g_snprintf(passwd_hash_text, sizeof(passwd_hash_text),
|
|
"%2.2x%2.2x%2.2x%2.2x",
|
|
(tui8)passwd_hash[0], (tui8)passwd_hash[1],
|
|
(tui8)passwd_hash[2], (tui8)passwd_hash[3]);
|
|
passwd = passwd_hash_text;
|
|
|
|
/* create file from password */
|
|
g_memset(encryptedPasswd, 0, sizeof(encryptedPasswd));
|
|
g_strncpy(encryptedPasswd, passwd, 8);
|
|
g_memset(key, 0, sizeof(key));
|
|
g_mirror_memcpy(key, g_fixedkey, 8);
|
|
des = ssl_des3_encrypt_info_create(key, 0);
|
|
ssl_des3_encrypt(des, 8, encryptedPasswd, encryptedPasswd);
|
|
ssl_des3_info_delete(des);
|
|
fd = g_file_open_ex(filename, 0, 1, 1, 1);
|
|
if (fd == -1)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Cannot write VNC password hash to file %s: %s",
|
|
filename, g_get_strerror());
|
|
return 1;
|
|
}
|
|
g_file_write(fd, encryptedPasswd, 8);
|
|
g_file_close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Prepare a list of parameters for the Xvnc X server
|
|
* @param sd Session data
|
|
* @param authfile XAUTHORITY file
|
|
* @param passwd_file VNC password file, or NULL
|
|
* @param port UDS port to connect to, or NULL
|
|
* @return parameters list
|
|
*
|
|
* One of passwd_file and port must be set
|
|
*/
|
|
static struct list *
|
|
prepare_xvnc_xserver_params(const struct session_data *sd,
|
|
const char *authfile,
|
|
const char *passwd_file,
|
|
const char *port)
|
|
{
|
|
char screen[32] = {0}; /* display number */
|
|
char geometry[32] = {0};
|
|
char depth[32] = {0};
|
|
const char *xserver;
|
|
const struct session_parameters *sp = &sd->params;
|
|
|
|
struct list *params = list_create();
|
|
if (params != NULL)
|
|
{
|
|
params->auto_free = 1;
|
|
|
|
g_snprintf(screen, sizeof(screen), ":%d", sp->x11_display);
|
|
g_snprintf(geometry, sizeof(geometry), "%dx%d", sp->width, sp->height);
|
|
g_snprintf(depth, sizeof(depth), "%d", sp->bpp);
|
|
|
|
/* get path of Xvnc from config */
|
|
xserver = (const char *)list_get_item(g_cfg->vnc_params, 0);
|
|
|
|
/* these are the must have parameters */
|
|
list_add_strdup_multi(params,
|
|
xserver, screen,
|
|
"-auth", authfile,
|
|
"-geometry", geometry,
|
|
"-depth", depth,
|
|
LIST_ADD_STRDUP_TERM);
|
|
|
|
if (passwd_file != NULL)
|
|
{
|
|
/* RFB authorization */
|
|
list_add_strdup_multi(params,
|
|
"-rfbauth", passwd_file,
|
|
LIST_ADD_STRDUP_TERM);
|
|
}
|
|
else if (port != NULL)
|
|
{
|
|
/* UDS connection. Authorization is handled by standard socket
|
|
* permissions, so we do not need to authorize within the
|
|
* VNC protocol exchange as well */
|
|
char sock_mode[16];
|
|
|
|
/* Convert a standard permissions mask into decimal
|
|
* for the -rfbunixmode switch argument
|
|
*/
|
|
g_snprintf(sock_mode, sizeof(sock_mode),
|
|
"%d", 0660); /* rw-rw---- */
|
|
|
|
list_add_strdup_multi(params,
|
|
"-rfbunixpath", port,
|
|
"-rfbunixmode", sock_mode,
|
|
"-SecurityTypes", "None",
|
|
LIST_ADD_STRDUP_TERM);
|
|
}
|
|
|
|
/* additional parameters from sesman.ini file */
|
|
//config_read_xserver_params(SCP_SESSION_TYPE_XVNC,
|
|
// xserver_params);
|
|
list_append_list_strdup(g_cfg->vnc_params, params, 1);
|
|
}
|
|
return params;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/* Either execs the X server, or returns */
|
|
static void
|
|
start_x_server(const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
void *closure /* unused */)
|
|
{
|
|
char authfile[256]; /* The filename for storing xauth information */
|
|
char execvpparams[2048];
|
|
char *passwd_file = NULL;
|
|
struct list *xserver_params = NULL;
|
|
int unknown_session_type = 0;
|
|
const struct session_parameters *sp = &sd->params;
|
|
|
|
env_set_user(login_info->uid,
|
|
g_cfg->env_names,
|
|
g_cfg->env_values);
|
|
|
|
/* Allocate the passwd_file if required */
|
|
if (sp->type == SCP_SESSION_TYPE_XVNC &&
|
|
(passwd_file = get_xvnc_passwd_file_name(sp->x11_display)) == NULL)
|
|
{
|
|
/* An error has been logged */
|
|
return;
|
|
}
|
|
|
|
/* prepare the Xauthority stuff */
|
|
if (g_getenv("XAUTHORITY") != NULL)
|
|
{
|
|
g_snprintf(authfile, sizeof(authfile), "%s",
|
|
g_getenv("XAUTHORITY"));
|
|
}
|
|
else
|
|
{
|
|
g_snprintf(authfile, sizeof(authfile), "%s", ".Xauthority");
|
|
}
|
|
|
|
/* Add the entry in XAUTHORITY file or exit if error */
|
|
if (sp->x11_display >= 0 &&
|
|
add_xauth_cookie(sp->x11_display, authfile) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Error setting the xauth cookie for display %s in file %s",
|
|
sd->display, authfile);
|
|
}
|
|
else
|
|
{
|
|
switch (sp->type)
|
|
{
|
|
char guid_str[GUID_STR_SIZE];
|
|
char port[256];
|
|
|
|
case SCP_SESSION_TYPE_XORG:
|
|
xserver_params = prepare_xorg_xserver_params(sd, authfile);
|
|
break;
|
|
|
|
case SCP_SESSION_TYPE_XVNC:
|
|
guid_to_str(&sp->guid, guid_str);
|
|
set_xvnc_passwd(passwd_file, guid_str);
|
|
xserver_params = prepare_xvnc_xserver_params(sd, authfile,
|
|
passwd_file, NULL);
|
|
break;
|
|
|
|
case SCP_SESSION_TYPE_XVNC_UDS:
|
|
g_snprintf(port, sizeof(port), XRDP_X11RDP_STR,
|
|
login_info->uid, sd->display);
|
|
xserver_params = prepare_xvnc_xserver_params(sd, authfile,
|
|
NULL, port);
|
|
break;
|
|
|
|
default:
|
|
unknown_session_type = 1;
|
|
}
|
|
|
|
if (xserver_params == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Out of memory allocating X server params");
|
|
}
|
|
else if (unknown_session_type)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Unknown session type: %d",
|
|
sp->type);
|
|
}
|
|
else
|
|
{
|
|
/* fire up X server */
|
|
LOG(LOG_LEVEL_INFO, "Starting X server on display %s: %s",
|
|
sd->display,
|
|
dumpItemsToString(xserver_params, execvpparams, 2048));
|
|
LOG_DEVEL_LEAKING_FDS("X server", 3, -1);
|
|
g_execvp_list((const char *)xserver_params->items[0],
|
|
xserver_params);
|
|
}
|
|
}
|
|
|
|
/* should not get here */
|
|
g_free(passwd_file);
|
|
list_delete(xserver_params);
|
|
LOG(LOG_LEVEL_ERROR, "A fatal error has occurred attempting "
|
|
"to start the X server on display %s, aborting connection",
|
|
sd->display);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Simple helper process to fork a child and log errors */
|
|
static int
|
|
fork_child(
|
|
void (*runproc)(const struct login_info *,
|
|
const struct session_data *,
|
|
void *closure),
|
|
const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
pid_t group_pid,
|
|
void *closure)
|
|
{
|
|
int pid = g_fork();
|
|
if (pid == 0)
|
|
{
|
|
/* Child process */
|
|
if (group_pid >= 0)
|
|
{
|
|
(void)g_setpgid(0, group_pid);
|
|
}
|
|
runproc(login_info, sd, closure);
|
|
g_exit(0);
|
|
}
|
|
|
|
if (pid < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Fork failed [%s]", g_get_strerror());
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
process_startup_wait_time(struct session_data *sd)
|
|
{
|
|
int rv = 0;
|
|
int robjs_count;
|
|
intptr_t robjs[10];
|
|
unsigned int start = g_get_elapsed_ms();
|
|
|
|
LOG(LOG_LEVEL_INFO, "Waiting for %u ms for session to start",
|
|
g_cfg->sess.startup_wait_time);
|
|
while (1)
|
|
{
|
|
unsigned int elapsed = g_get_elapsed_ms() - start;
|
|
if (elapsed >= g_cfg->sess.startup_wait_time)
|
|
{
|
|
break;
|
|
}
|
|
|
|
robjs_count = 0;
|
|
robjs[robjs_count++] = g_term_event;
|
|
robjs[robjs_count++] = g_sigchld_event;
|
|
|
|
if (g_obj_wait(robjs, robjs_count, NULL, 0,
|
|
g_cfg->sess.startup_wait_time - elapsed) != 0)
|
|
{
|
|
/* should not get here */
|
|
LOG(LOG_LEVEL_WARNING, "process_startup_wait_time: "
|
|
"Unexpected error from g_obj_wait()");
|
|
g_sleep(100);
|
|
continue;
|
|
}
|
|
|
|
if (g_is_wait_obj_set(g_term_event)) /* term */
|
|
{
|
|
// Simulate success for now, but leave g_term_event set. The
|
|
// main loop will also pick up the terminate event and the
|
|
// session will be closed normally
|
|
break;
|
|
}
|
|
|
|
if (g_is_wait_obj_set(g_sigchld_event)) /* SIGCHLD */
|
|
{
|
|
g_reset_wait_obj(g_sigchld_event);
|
|
session_process_sigchld_event(sd);
|
|
if (sd->win_mgr < 0)
|
|
{
|
|
// Session has failed in the StartupWaitTime
|
|
// Wait for the rest of the session to finish
|
|
rv = 1;
|
|
session_send_term(sd, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static enum scp_screate_status
|
|
session_start_wrapped(struct login_info *login_info,
|
|
const struct session_parameters *s,
|
|
struct session_data *sd)
|
|
{
|
|
int chansrv_pid;
|
|
int display_pid;
|
|
int window_manager_pid;
|
|
enum scp_screate_status status = E_SCP_SCREATE_GENERAL_ERROR;
|
|
|
|
/* Set the secondary groups before starting the session to prevent
|
|
* problems on PAM-based systems (see Linux pam_setcred(3)).
|
|
* If we have *BSD setusercontext() this is not done here */
|
|
#ifndef HAVE_SETUSERCONTEXT
|
|
if (g_initgroups(login_info->username) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Failed to initialise secondary groups for %s: %s",
|
|
login_info->username, g_get_strerror());
|
|
return E_SCP_SCREATE_GENERAL_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (auth_start_session(login_info->auth_info, sd->display) != 0)
|
|
{
|
|
// Errors are logged by the auth module, as they are
|
|
// specific to that module
|
|
return E_SCP_SCREATE_GENERAL_ERROR;
|
|
}
|
|
#ifdef USE_BSD_SETLOGIN
|
|
/**
|
|
* Create a new session and process group since the 4.4BSD
|
|
* setlogin() affects the entire process group
|
|
*/
|
|
if (g_setsid() < 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"[session start] (display %s): setsid failed - pid %d",
|
|
sd->display, g_getpid());
|
|
}
|
|
|
|
if (g_setlogin(login_info->username) < 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"[session start] (display %s): setlogin failed for user %s - pid %d",
|
|
sd->display, login_info->username, g_getpid());
|
|
}
|
|
#endif
|
|
|
|
/* start the X server in a new process group.
|
|
*
|
|
* We group the X server, window manager and chansrv in a single
|
|
* process group, as it allows signals to be sent to the user session
|
|
* without affecting sesexec (and vice-versa). This is particularly
|
|
* important when debugging sesexec as we don't want a SIGINT in
|
|
* the debugger to be passed to the children */
|
|
display_pid = fork_child(start_x_server, login_info, sd, 0, NULL);
|
|
if (display_pid > 0)
|
|
{
|
|
enum xwait_status xws;
|
|
xws = wait_for_xserver(login_info->uid,
|
|
g_cfg->env_names,
|
|
g_cfg->env_values,
|
|
s->x11_display);
|
|
|
|
if (xws != XW_STATUS_OK)
|
|
{
|
|
switch (xws)
|
|
{
|
|
case XW_STATUS_TIMED_OUT:
|
|
LOG(LOG_LEVEL_ERROR, "Timed out waiting for X server");
|
|
break;
|
|
case XW_STATUS_FAILED_TO_START:
|
|
LOG(LOG_LEVEL_ERROR, "X server failed to start");
|
|
break;
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"An error occurred waiting for the X server");
|
|
}
|
|
status = E_SCP_SCREATE_X_SERVER_FAIL;
|
|
/* Kill it anyway in case it did start and we just failed to
|
|
* pick up on it */
|
|
g_sigterm(display_pid);
|
|
g_waitpid(display_pid);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_INFO, "Display %s is working", sd->display);
|
|
LOG(LOG_LEVEL_INFO, "Starting window manager for display %s",
|
|
sd->display);
|
|
|
|
window_manager_pid = fork_child(start_window_manager,
|
|
login_info, sd, display_pid, NULL);
|
|
if (window_manager_pid < 0)
|
|
{
|
|
g_sigterm(display_pid);
|
|
g_waitpid(display_pid);
|
|
}
|
|
else
|
|
{
|
|
utmp_login(window_manager_pid, sd->display, login_info);
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Starting the xrdp channel server for display %s",
|
|
sd->display);
|
|
|
|
chansrv_pid = fork_child(start_chansrv, login_info,
|
|
sd, display_pid, NULL);
|
|
|
|
sd->win_mgr = window_manager_pid;
|
|
sd->x_server = display_pid;
|
|
sd->chansrv = chansrv_pid;
|
|
sd->start_time = time(NULL);
|
|
|
|
if (process_startup_wait_time(sd) == 0)
|
|
{
|
|
// Tell the caller we've started
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Session in progress on display %s. Waiting until the "
|
|
"window manager (pid %d) exits to end the session",
|
|
sd->display, window_manager_pid);
|
|
|
|
status = E_SCP_SCREATE_OK;
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Session failed during startup wait time");
|
|
status = E_SCP_SCREATE_SESSION_FAIL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/******************************************************************************/
|
|
enum scp_screate_status
|
|
session_start(struct login_info *login_info,
|
|
const struct session_parameters *sp,
|
|
struct session_data **session_data)
|
|
{
|
|
enum scp_screate_status status = E_SCP_SCREATE_OK;
|
|
|
|
/* Create the session_data struct first */
|
|
struct session_data *sd = session_data_new(sp);
|
|
if (sd == NULL)
|
|
{
|
|
status = E_SCP_SCREATE_NO_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
if (sp->x11_display >= 0)
|
|
{
|
|
/* Initialise the display name for logging purposes */
|
|
g_get_display_string_from_x11_display(sp->x11_display,
|
|
sd->display,
|
|
MAX_DISPLAY_NAME_SIZE);
|
|
/* Add the DISPLAY to the list of environment variables we
|
|
* set for all the sub-processes */
|
|
char displayname[32];
|
|
snprintf(displayname, sizeof(displayname), ":%d",
|
|
sp->x11_display);
|
|
|
|
if (!list_add_strdup(g_cfg->env_names, "DISPLAY") ||
|
|
!list_add_strdup(g_cfg->env_values, displayname))
|
|
{
|
|
session_data_free(sd);
|
|
status = E_SCP_SCREATE_NO_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (status == E_SCP_SCREATE_OK)
|
|
{
|
|
status = session_start_wrapped(login_info, sp, sd);
|
|
if (status == E_SCP_SCREATE_OK)
|
|
{
|
|
*session_data = sd;
|
|
}
|
|
else
|
|
{
|
|
*session_data = NULL;
|
|
session_data_free(sd);
|
|
}
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static int
|
|
cleanup_sockets(struct session_data *sd)
|
|
{
|
|
LOG_DEVEL(LOG_LEVEL_INFO, "cleanup_sockets:");
|
|
|
|
char file[XRDP_SOCKETS_MAXPATH];
|
|
int error = 0;
|
|
|
|
int uid = g_login_info->uid;
|
|
|
|
g_snprintf(file, sizeof(file), CHANSRV_PORT_OUT_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
g_snprintf(file, sizeof(file), CHANSRV_PORT_IN_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
g_snprintf(file, sizeof(file), XRDP_CHANSRV_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
g_snprintf(file, sizeof(file), CHANSRV_API_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
/* the following files should be deleted by xorgxrdp
|
|
* but just in case the deletion failed */
|
|
|
|
g_snprintf(file, sizeof(file), XRDP_X11RDP_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
g_snprintf(file, sizeof(file), XRDP_DISCONNECT_STR, uid, sd->display);
|
|
if (g_file_exist(file))
|
|
{
|
|
LOG(LOG_LEVEL_DEBUG, "cleanup_sockets: deleting %s", file);
|
|
if (g_file_delete(file) == 0)
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"cleanup_sockets: failed to delete %s (%s)",
|
|
file, g_get_strerror());
|
|
error++;
|
|
}
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static void
|
|
exit_status_to_str(const struct proc_exit_status *e, char buff[], int bufflen)
|
|
{
|
|
switch (e->reason)
|
|
{
|
|
case E_PXR_STATUS_CODE:
|
|
if (e->val == 0)
|
|
{
|
|
g_snprintf(buff, bufflen, "exit code zero");
|
|
}
|
|
else
|
|
{
|
|
g_snprintf(buff, bufflen, "non-zero exit code %d", e->val);
|
|
}
|
|
break;
|
|
|
|
case E_PXR_SIGNAL:
|
|
{
|
|
char sigstr[MAXSTRSIGLEN];
|
|
g_snprintf(buff, bufflen, "signal %s",
|
|
g_sig2text(e->val, sigstr));
|
|
}
|
|
break;
|
|
|
|
default:
|
|
g_snprintf(buff, bufflen, "an unexpected error");
|
|
break;
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/**
|
|
* Processes an exited child
|
|
*
|
|
* The PID of the child process is removed from the session_data.
|
|
*
|
|
* @param sd session_data for this session
|
|
* @param pid PID of exited process
|
|
* @param e Exit status of the exited process
|
|
*/
|
|
static void
|
|
process_child_exit(struct session_data *sd,
|
|
int pid,
|
|
const struct proc_exit_status *e)
|
|
{
|
|
if (pid == sd->x_server)
|
|
{
|
|
LOG(LOG_LEVEL_INFO, "X server pid %d on display %s finished",
|
|
sd->x_server, sd->display);
|
|
sd->x_server = -1;
|
|
// No other action - window manager should be going soon
|
|
}
|
|
else if (pid == sd->chansrv)
|
|
{
|
|
LOG(LOG_LEVEL_INFO,
|
|
"xrdp channel server pid %d on display %s finished",
|
|
sd->chansrv, sd->display);
|
|
sd->chansrv = -1;
|
|
}
|
|
else if (pid == sd->win_mgr)
|
|
{
|
|
int wm_wait_time = time(NULL) - sd->start_time;
|
|
|
|
if (e->reason == E_PXR_STATUS_CODE && e->val == 0)
|
|
{
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Window manager (pid %d, display %s) "
|
|
"finished normally in %d secs",
|
|
sd->win_mgr, sd->display, wm_wait_time);
|
|
}
|
|
else
|
|
{
|
|
char reason[128];
|
|
exit_status_to_str(e, reason, sizeof(reason));
|
|
|
|
LOG(LOG_LEVEL_WARNING, "Window manager (pid %d, display %s) "
|
|
"exited with %s. This "
|
|
"could indicate a window manager config problem",
|
|
sd->win_mgr, sd->display, reason);
|
|
}
|
|
if (wm_wait_time < 10)
|
|
{
|
|
/* This could be a config issue. Log a significant error */
|
|
LOG(LOG_LEVEL_WARNING, "Window manager (pid %d, display %s) "
|
|
"exited quickly (%d secs). This could indicate a window "
|
|
"manager config problem",
|
|
sd->win_mgr, sd->display, wm_wait_time);
|
|
}
|
|
|
|
utmp_logout(sd->win_mgr, sd->display, e);
|
|
sd->win_mgr = -1;
|
|
|
|
if (sd->x_server > 0)
|
|
{
|
|
LOG(LOG_LEVEL_INFO, "Terminating X server (pid %d) on display %s",
|
|
sd->x_server, sd->display);
|
|
g_sigterm(sd->x_server);
|
|
}
|
|
|
|
if (sd->chansrv > 0)
|
|
{
|
|
LOG(LOG_LEVEL_INFO, "Terminating the xrdp channel server (pid %d) "
|
|
"on display %s", sd->chansrv, sd->display);
|
|
g_sigterm(sd->chansrv);
|
|
}
|
|
}
|
|
|
|
if (!session_active(sd))
|
|
{
|
|
cleanup_sockets(sd);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_process_sigchld_event(struct session_data *sd)
|
|
{
|
|
struct proc_exit_status e;
|
|
int pid;
|
|
|
|
// Check for any finished children
|
|
while ((pid = g_waitchild(&e)) > 0)
|
|
{
|
|
process_child_exit(sd, pid, &e);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
unsigned int
|
|
session_active(const struct session_data *sd)
|
|
{
|
|
return
|
|
(sd == NULL)
|
|
? 0
|
|
: (sd->win_mgr > 0) + (sd->x_server > 0) + (sd->chansrv > 0);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
time_t
|
|
session_get_start_time(const struct session_data *sd)
|
|
{
|
|
return (sd == NULL) ? 0 : sd->start_time;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
unsigned int
|
|
session_get_connect_count(const struct session_data *sd)
|
|
{
|
|
return (sd == NULL) ? 0 : sd->connect_count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
const char *
|
|
session_get_display(const struct session_data *sd)
|
|
{
|
|
return (sd == NULL) ? "" : sd->display;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
unsigned int
|
|
session_increment_connect_count(struct session_data *sd)
|
|
{
|
|
return (sd == NULL) ? 0 : sd->connect_count++;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
const struct session_parameters *
|
|
session_get_parameters(const struct session_data *sd)
|
|
{
|
|
return (sd == NULL) ? NULL : &sd->params;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_send_term(struct session_data *sd, int wait_for_all)
|
|
{
|
|
if (sd != NULL)
|
|
{
|
|
if (sd->win_mgr > 0)
|
|
{
|
|
// Killing the window manager only is appropriate here.
|
|
// When we process SIGCHLD for the window manager, we
|
|
// will kill other processes as appropriate
|
|
g_sigterm(sd->win_mgr);
|
|
}
|
|
|
|
if (wait_for_all)
|
|
{
|
|
while (session_active(sd))
|
|
{
|
|
/* Don't check SIGTERM - we shouldn't be here long */
|
|
if (g_obj_wait(&g_sigchld_event, 1, NULL, 0, -1) != 0)
|
|
{
|
|
/* should not get here */
|
|
LOG(LOG_LEVEL_WARNING, "session_send_term: "
|
|
"Unexpected error from g_obj_wait()");
|
|
g_sleep(100);
|
|
}
|
|
else
|
|
{
|
|
g_reset_wait_obj(g_sigchld_event);
|
|
session_process_sigchld_event(sd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
static void
|
|
start_reconnect_script(const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
void *closure)
|
|
{
|
|
env_set_user(login_info->uid,
|
|
g_cfg->env_names,
|
|
g_cfg->env_values);
|
|
|
|
auth_set_env(login_info->auth_info);
|
|
|
|
if (g_file_exist(g_cfg->reconnect_sh))
|
|
{
|
|
/* The 'closure' parameter points to a list of strings
|
|
* which need to be set in the environment for the reconnect script */
|
|
if (closure != NULL)
|
|
{
|
|
const char **p = (const char **)closure;
|
|
while (*p != NULL && *(p + 1) != NULL)
|
|
{
|
|
(void)g_setenv(*p, *(p + 1), 1);
|
|
p += 2;
|
|
}
|
|
}
|
|
LOG_DEVEL_LEAKING_FDS("reconnect script", 3, -1);
|
|
|
|
LOG(LOG_LEVEL_INFO,
|
|
"Starting session reconnection script on display %s: %s",
|
|
sd->display, g_cfg->reconnect_sh);
|
|
g_execlp3(g_cfg->reconnect_sh, g_cfg->reconnect_sh, 0);
|
|
|
|
/* should not get here */
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Error starting session reconnection script on display %s: %s",
|
|
sd->display, g_cfg->reconnect_sh);
|
|
}
|
|
else
|
|
{
|
|
LOG(LOG_LEVEL_WARNING,
|
|
"Session reconnection script file does not exist: %s",
|
|
g_cfg->reconnect_sh);
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
void
|
|
session_run_reconnect_script(const struct login_info *login_info,
|
|
const struct session_data *sd,
|
|
const char *vars[])
|
|
{
|
|
if (fork_child(start_reconnect_script,
|
|
login_info, sd, sd->x_server, (void *)vars) < 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Failed to fork for session reconnection script");
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
session_get_display_server_fd(const struct login_info *login_info,
|
|
const struct session_data *sd)
|
|
{
|
|
char portname[XRDP_SOCKETS_MAXPATH];
|
|
const char *localhost = "localhost"; // Ignored for TRANS_MODE_UNIX
|
|
int socket_mode;
|
|
|
|
int rv = -1;
|
|
|
|
if (sd->x_server <= 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Request to connect to display server %s"
|
|
" which has exited", sd->display);
|
|
}
|
|
else
|
|
{
|
|
switch (sd->params.type)
|
|
{
|
|
case SCP_SESSION_TYPE_XVNC:
|
|
socket_mode = TRANS_MODE_TCP;
|
|
snprintf(portname, sizeof(portname), "%d",
|
|
5900 + sd->params.x11_display);
|
|
break;
|
|
|
|
case SCP_SESSION_TYPE_XVNC_UDS:
|
|
case SCP_SESSION_TYPE_XORG:
|
|
socket_mode = TRANS_MODE_UNIX;
|
|
snprintf(portname, sizeof(portname), XRDP_X11RDP_STR,
|
|
login_info->uid, sd->display);
|
|
|
|
break;
|
|
|
|
default:
|
|
LOG(LOG_LEVEL_ERROR, "Unsupported session type %d for connect",
|
|
sd->params.type);
|
|
portname[0] = '\0';
|
|
}
|
|
|
|
if (portname[0] != '\0')
|
|
{
|
|
// Use the transport library to get the fd
|
|
struct trans *t = trans_create(socket_mode, 8 * 8192, 8192);
|
|
if (t == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Out of memory creating transport");
|
|
}
|
|
else if (trans_connect(t, localhost, portname, 3000) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't connect to display server %s [%s]",
|
|
sd->display,
|
|
g_get_strerror());
|
|
}
|
|
else
|
|
{
|
|
rv = t->sck;
|
|
t->sck = -1;
|
|
}
|
|
trans_delete(t);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
int
|
|
session_get_chansrv_fd(const struct login_info *login_info,
|
|
const struct session_data *sd)
|
|
{
|
|
char portname[XRDP_SOCKETS_MAXPATH];
|
|
|
|
int rv = -1;
|
|
|
|
if (sd->chansrv <= 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR,
|
|
"Request to connect to chansrv %s"
|
|
" which has exited", sd->display);
|
|
}
|
|
else
|
|
{
|
|
snprintf(portname, sizeof(portname),
|
|
XRDP_CHANSRV_STR, login_info->uid, sd->display);
|
|
|
|
// Use the transport library to get the fd
|
|
struct trans *t = trans_create(TRANS_MODE_UNIX, 8192, 8192);
|
|
if (t == NULL)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Out of memory creating transport");
|
|
}
|
|
else if (trans_connect(t, NULL, portname, 10 * 1000) != 0)
|
|
{
|
|
LOG(LOG_LEVEL_ERROR, "Can't connect to chansrv %s [%s]",
|
|
sd->display,
|
|
g_get_strerror());
|
|
}
|
|
else
|
|
{
|
|
rv = t->sck;
|
|
t->sck = -1;
|
|
}
|
|
trans_delete(t);
|
|
}
|
|
|
|
return rv;
|
|
}
|