Merge pull request #2974 from matt335672/xrdp_as_unprivileged_user

Xrdp as unprivileged user
This commit is contained in:
matt335672 2024-07-02 08:56:55 +01:00 committed by GitHub
commit fced0002bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 706 additions and 247 deletions

View File

@ -3167,6 +3167,48 @@ g_setgid(int pid)
#endif
}
/*****************************************************************************/
/* Used by daemonizing code */
/* returns error, zero is success, non zero is error */
int
g_drop_privileges(const char *user, const char *group)
{
int rv = 1;
int uid;
int gid;
if (g_getuser_info_by_name(user, &uid, NULL, NULL, NULL, NULL) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to get UID for user '%s' [%s]", user,
g_get_strerror());
}
else if (g_getgroup_info(group, &gid) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to get GID for group '%s' [%s]", group,
g_get_strerror());
}
else if (initgroups(user, gid) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to init groups for '%s' [%s]", user,
g_get_strerror());
}
else if (g_setgid(gid) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to set group to '%s' [%s]", group,
g_get_strerror());
}
else if (g_setuid(uid) != 0)
{
LOG(LOG_LEVEL_ERROR, "Unable to set user to '%s' [%s]", user,
g_get_strerror());
}
else
{
rv = 0;
}
return rv;
}
/*****************************************************************************/
/* returns error, zero is success, non zero is error */
/* does not work in win32 */
@ -3496,6 +3538,12 @@ g_sigterm(int pid)
#endif
}
/*****************************************************************************/
int g_pid_is_active(int pid)
{
return (kill(pid, 0) == 0);
}
/*****************************************************************************/
/* does not work in win32 */
int

View File

@ -338,6 +338,7 @@ void g_signal_pipe(void (*func)(int));
void g_signal_usr1(void (*func)(int));
int g_fork(void);
int g_setgid(int pid);
int g_drop_privileges(const char *user, const char *group);
int g_initgroups(const char *user);
int g_getuid(void);
int g_getgid(void);
@ -371,6 +372,12 @@ int g_exit(int exit_code);
int g_getpid(void);
int g_sigterm(int pid);
int g_sighup(int pid);
/*
* Is a particular PID active?
* @param pid PID to check
* Returns boolean
*/
int g_pid_is_active(int pid);
int g_getuser_info_by_name(const char *username, int *uid, int *gid,
char **shell, char **dir, char **gecos);
int g_getuser_info_by_uid(int uid, char **username, int *gid,

View File

@ -122,8 +122,11 @@ tc_mutex_delete(tbus mutex)
pthread_mutex_t *lmutex;
lmutex = (pthread_mutex_t *)mutex;
pthread_mutex_destroy(lmutex);
g_free(lmutex);
if (lmutex != NULL)
{
pthread_mutex_destroy(lmutex);
g_free(lmutex);
}
#endif
}

View File

@ -646,6 +646,7 @@ AC_CONFIG_FILES([
tools/Makefile
tools/devel/Makefile
tools/devel/tcp_proxy/Makefile
tools/chkpriv/Makefile
vnc/Makefile
xrdpapi/Makefile
xrdp/Makefile

View File

@ -315,7 +315,8 @@ transitions between confinement domains.
.TP
\fBSessionSockdirGroup\fR=\fIgroup\fR
Sets the group owner of the directories containing session sockets. This
is normally the GID of the xrdp process so xrdp can connect to user sessions.
MUST be the same as runtime_group in xrdp.ini, or xrdp will not
be able to connect to any sessions.
.SH "X11 SERVER"
Following parameters can be used in the \fB[Xvnc]\fR and

View File

@ -119,6 +119,29 @@ The default port for RDP is \fB3389\fP.
Multiple address:port instances must be separated by spaces or commas. Check the .ini file for examples.
Specifying interfaces requires said interfaces to be UP before xrdp starts.
.TP
\fBruntime_user\fP=\fIusername\fP
.TP
\fBruntime_group\fP=\fIgroupname\fP
User name and group to run the xrdp daemon under.
After xrdp starts, it sets its UID and GID to values derived from these
settings, so that it's running without system privilege.
The \fBruntime_group\fP MUST be set to the same value as
\fBSessionSockdirGroup\fP in \fBsesman.ini\fP if you want to run sessions.
A suitable user and group can be added with a command like this (Linux):-
useradd xrdp -d / -c 'xrdp daemon' -s /usr/sbin/nologin
In order to establish secure connections, the xrdp daemon needs permission
to access sensitive cryptographic files. After changing either or both
of these values, check that xrdp has access to required files by running
this script:-
@xrdpdatadir@/xrdp-chkpriv
.TP
\fBenable_token_login\fP=\fI[true|false]\fP
If set to \fB1\fP, \fBtrue\fP or \fByes\fP, \fBxrdp\fP will scan the user name provided by the

View File

@ -98,6 +98,9 @@ endif
if FREEBSD
# must be tab below
install-data-hook:
sed -i '' 's|%%PREFIX%%|$(prefix)|g' $(DESTDIR)$(sysconfdir)/rc.d/xrdp \
$(DESTDIR)$(sysconfdir)/rc.d/xrdp-sesman
sed -e 's|%%PREFIX%%|$(prefix)|g' \
-e 's|%%LOCALSTATEDIR%%|$(localstatedir)|g' \
-i '' \
$(DESTDIR)$(sysconfdir)/rc.d/xrdp \
$(DESTDIR)$(sysconfdir)/rc.d/xrdp-sesman
endif

View File

@ -116,7 +116,7 @@ case "$1" in
log_progress_msg $NAME
if pidofproc -p $PIDDIR/$NAME.pid $DAEMON > /dev/null; then
start-stop-daemon --stop --quiet --oknodo --pidfile $PIDDIR/$NAME.pid \
--exec $DAEMON
--remove-pidfile --exec $DAEMON
value=$?
[ $value -gt 0 ] && exitval=$value
else

View File

@ -48,6 +48,7 @@ command="%%PREFIX%%/sbin/xrdp"
allstart_cmd="xrdp_allstart"
allstop_cmd="xrdp_allstop"
allrestart_cmd="xrdp_allrestart"
stop_postcmd="xrdp_poststop"
xrdp_allstart()
{
@ -79,4 +80,10 @@ xrdp_allrestart()
run_rc_command "restart"
}
xrdp_poststop()
{
# If running with dropped privileges, xrdp can't delete its own
# PID file
rm -f %%LOCALSTATEDIR%%/run/xrdp.pid
}
run_rc_command "$1"

View File

@ -45,10 +45,10 @@ RestrictInboundClipboard=none
; Leave this unset unless you need to disable it.
#XorgNoNewPrivileges=true
; Specify the group which is to have read access to the directory where
; local sockets for the session are created. This is normally the GID
; which the xrdp process runs as.
; Default is 'root'
#SessionSockdirGroup=root
; local sockets for the session are created.
; This MUST be the same as runtime_group in xrdp.ini, or xrdp will not
; be able to connect to your sessions.
#SessionSockdirGroup=xrdp
[Sessions]

View File

@ -1,3 +1,4 @@
SUBDIRS = \
chkpriv \
devel

31
tools/chkpriv/Makefile.am Normal file
View File

@ -0,0 +1,31 @@
xrdppkgdatadir=$(datadir)/xrdp
pkglibexec_PROGRAMS = \
xrdp-droppriv
dist_xrdppkgdata_SCRIPTS = \
xrdp-chkpriv
AM_LDFLAGS =
AM_CPPFLAGS = \
-I$(top_srcdir)/common
xrdp_droppriv_SOURCES = xrdp-droppriv.c
xrdp_droppriv_LDADD = \
$(top_builddir)/common/libcommon.la
SUBST_VARS = sed \
-e 's|@pkglibexecdir[@]|$(pkglibexecdir)|g'
subst_verbose = $(subst_verbose_@AM_V@)
subst_verbose_ = $(subst_verbose_@AM_DEFAULT_V@)
subst_verbose_0 = @echo " SUBST $@";
SUFFIXES = .in
.in:
$(subst_verbose)$(SUBST_VARS) $< > $@
CLEANFILES = xrdp-chkpriv

199
tools/chkpriv/xrdp-chkpriv.in Executable file
View File

@ -0,0 +1,199 @@
#!/bin/sh
#
# xrdp: A Remote Desktop Protocol server.
#
# Copyright (C) Jay Sorg and contributors 2004-2024
#
# 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.
#
# Program to check permissions for xrdp when running in a non-privileged
# mode
# Change these if they do not match your installation
CONF_DIR=/etc/xrdp
XRDP_INI="$CONF_DIR"/xrdp.ini
SESMAN_INI="$CONF_DIR"/sesman.ini
RSAKEYS_INI="$CONF_DIR"/rsakeys.ini
DROPPRIV=@pkglibexecdir@/xrdp-droppriv
# -----------------------------------------------------------------------------
# G E T I N I V A L U E
#
# Gets a value from an ini file.
#
# Params [ini_file] [key]
# -----------------------------------------------------------------------------
GetIniValue()
{
# Look for a line matching 'key=' with optional whitespace
# either side of the key. When we find one, strip everything
# up to and including the first '=', print it, and quit
#
# This doesn't take sections into account
sed -n -e '/^ *'"$2"' *=/{
s/^[^=]*=//p
q
}' "$1"
}
# -----------------------------------------------------------------------------
# M A I N
# -----------------------------------------------------------------------------
if [ "$(id -u)" != 0 ]; then
echo "** Must run this script as root" >&2
exit 1
fi
OS=$(uname)
case "$OS" in
FreeBSD | Linux) ;;
*) echo "Unsupported operating system $OS" >&2
exit 1
esac
errors=0
runtime_user=$(GetIniValue "$XRDP_INI" runtime_user)
runtime_group=$(GetIniValue "$XRDP_INI" runtime_group)
certificate=$(GetIniValue "$XRDP_INI" certificate)
key_file=$(GetIniValue "$XRDP_INI" key_file)
SessionSockdirGroup=$(GetIniValue "$SESMAN_INI" SessionSockdirGroup)
case "$certificate" in
'') certificate="$CONF_DIR"/cert.pem ;;
/*) ;;
*) certificate="$CONF_DIR"/"$certificate"
esac
case "$key_file" in
'') key_file="$CONF_DIR"/key.pem ;;
/*) ;;
*) key_file="$CONF_DIR"/"$key_file"
esac
echo "Settings"
echo " - [xrdp.ini] runtime_user : $runtime_user"
echo " - [xrdp.ini] runtime_group : $runtime_group"
echo " - [xrdp.ini] certificate : $certificate"
echo " - [xrdp.ini] key_file : $key_file"
echo " - [sesman.ini] SessionSockdirGroup : $SessionSockdirGroup"
echo
# Basic checks on runtime user/group
if [ -z "$runtime_user" ] && [ -z "$runtime_group" ]; then
echo "-Info- This system is not configured to run xrdp without privilege"
exit 0
fi
if [ -z "$runtime_user" ] || [ -z "$runtime_group" ]; then
echo "-Error- Both 'runtime_user' and 'runtime_group' must be set"
errors=$(( errors + 1 ))
exit 1
fi
if getent passwd "$runtime_user" >/dev/null ; then
echo "-Info- runtime_user '$runtime_user' appears to exist"
else
echo "-Error- runtime_user '$runtime_user' does not exist"
errors=$(( errors + 1 ))
fi
GID=
if getent group "$runtime_group" >/dev/null ; then
echo "-Info- runtime_group '$runtime_group' appears to exist"
GID=$(getent group xrdp | cut -d: -f3)
else
echo "-Error- runtime_group '$runtime_group' does not exist"
errors=$(( errors + 1 ))
fi
# Groups agree between sesman and xrdp?
if [ "$runtime_user" = "$SessionSockdirGroup" ]; then
echo "-Info- xrdp.ini and sesman.ini agree on group ownership"
else
echo "-Error- xrdp.ini and sesman.ini do not agree on group ownership"
errors=$(( errors + 1 ))
fi
# Check we can access rsakeys.ini
#
# This is our file, so we can be completely prescriptive about
# the permissions
if [ -e $RSAKEYS_INI ]; then
# Only check if we have a GID
if [ -n "$GID" ]; then
# Get the permissions, UID and GID in $1..$3
case "$OS" in
FreeBSD)
# shellcheck disable=SC2046
set -- $(stat -f "%Lp %u %g" $RSAKEYS_INI)
;;
*)
# shellcheck disable=SC2046
set -- $(stat -c "%a %u %g" $RSAKEYS_INI)
esac
if [ "$1/$2/$3" = "640/0/$GID" ]; then
echo "-Info- $RSAKEYS_INI has correct permissions"
else
if [ "$1" != 640 ]; then
echo "-Error- $RSAKEYS_INI should have permissions -rw-r-----"
errors=$(( errors + 1 ))
fi
if [ "$2" != 0 ]; then
echo "-Error- $RSAKEYS_INI should be owned by root"
errors=$(( errors + 1 ))
fi
if [ "$3" != "$GID" ]; then
echo "-Error- $RSAKEYS_INI should be in the $runtime_group group"
errors=$(( errors + 1 ))
fi
fi
fi
else
echo "-Error- $RSAKEYS_INI does not exist"
errors=$(( errors + 1 ))
fi
# Are cert and key readable (but NOT writeable) by the user?
#
# These aren't necessarily our files, so we can't be too prescriptive about
# privileges. On Debian for example, we might be using the 'ssl-cert'
# group to obtain access to /etc/ssl/private/ssl-cert-snakeoil.key
for file in "$certificate" "$key_file"; do
if ! [ -e $file ]; then
echo "-Error- $file does not exist"
errors=$(( errors + 1 ))
elif ! $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -r '"$file"' ]'
then
echo "-Error- $file is not readable by $runtime_user:$runtime_group"
errors=$(( errors + 1 ))
elif $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -w '"$file"' ]'
then
echo "-Error- $file is writeable by $runtime_user:$runtime_group"
errors=$(( errors + 1 ))
else
echo "-Info- $file is read-only for $runtime_user:$runtime_group"
fi
done
echo
if [ $errors -eq 0 ]; then
echo "-Summary- Permissions appear to be correct to run xrdp unprivileged"
status=0
else
echo "-Summary- $errors error(s) found. Please correct these and try again"
status=1
fi
exit $status

49
tools/chkpriv/xrdp-droppriv.c Executable file
View File

@ -0,0 +1,49 @@
/*
*
* xrdp: A Remote Desktop Protocol server.
*
* Copyright (C) Jay Sorg and contributors 2004-2024
*
* 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.
*
* Shell around the g_drop_privileges() call
*/
#if defined(HAVE_CONFIG_H)
#include "config_ac.h"
#endif
#include "os_calls.c"
#include "log.h"
int main(int argc, char *argv[])
{
struct log_config *logging;
int status = 1;
logging = log_config_init_for_console(LOG_LEVEL_WARNING,
g_getenv("DROPPRIV_LOG_LEVEL"));
log_start_from_param(logging);
log_config_free(logging);
if (argc < 4)
{
LOG(LOG_LEVEL_ERROR, "Usage : %s [user] [group] [cmd...]\n", argv[0]);
}
else if (g_drop_privileges(argv[1], argv[2]) == 0)
{
status = g_execvp(argv[3], &argv[3]);
}
log_end();
return status;
}

View File

@ -240,6 +240,127 @@ xrdp_process_params(int argc, char **argv,
return 0;
}
/*****************************************************************************/
/**
*
* @brief Read additional startup parameters from xrdp.ini
*
* @param [in,out] startup parameters from the command line
* @return 0 on success
*
*/
static int
read_xrdp_ini_startup_params(struct xrdp_startup_params *startup_params)
{
int rv = 0;
int fd;
int index;
int port_override;
int fork_override;
const char *name;
const char *val;
struct list *names;
struct list *values;
port_override = startup_params->port[0] != 0;
fork_override = startup_params->fork;
names = list_create();
names->auto_free = 1;
values = list_create();
values->auto_free = 1;
fd = g_file_open_ro(startup_params->xrdp_ini);
if (fd < 0)
{
LOG(LOG_LEVEL_ERROR, "Can't open %s [%s]", startup_params->xrdp_ini,
g_get_strerror());
rv = 1;
}
else if (file_read_section(fd, "globals", names, values) != 0)
{
LOG(LOG_LEVEL_ERROR, "Can't read [Globals] from %s",
startup_params->xrdp_ini);
rv = 1;
}
else
{
for (index = 0; index < names->count; index++)
{
name = (const char *)list_get_item(names, index);
val = (const char *)list_get_item(values, index);
if (name == 0 || val == 0)
{
continue;
}
if (g_strcasecmp(name, "port") == 0)
{
if (port_override == 0)
{
g_strncpy(startup_params->port, val,
sizeof(startup_params->port) - 1);
}
}
else if (g_strcasecmp(name, "fork") == 0)
{
if (fork_override == 0)
{
startup_params->fork = g_text2bool(val);
}
}
else if (g_strcasecmp(name, "tcp_nodelay") == 0)
{
startup_params->tcp_nodelay = g_text2bool(val);
}
else if (g_strcasecmp(name, "tcp_keepalive") == 0)
{
startup_params->tcp_keepalive = g_text2bool(val);
}
else if (g_strcasecmp(name, "tcp_send_buffer_bytes") == 0)
{
startup_params->tcp_send_buffer_bytes = g_atoi(val);
}
else if (g_strcasecmp(name, "tcp_recv_buffer_bytes") == 0)
{
startup_params->tcp_recv_buffer_bytes = g_atoi(val);
}
else if (g_strcasecmp(name, "use_vsock") == 0)
{
startup_params->use_vsock = g_text2bool(val);
}
else if (g_strcasecmp(name, "runtime_user") == 0)
{
g_snprintf(startup_params->runtime_user,
sizeof(startup_params->runtime_user),
"%s", val);
}
else if (g_strcasecmp(name, "runtime_group") == 0)
{
g_snprintf(startup_params->runtime_group,
sizeof(startup_params->runtime_group),
"%s", val);
}
}
}
list_delete(names);
list_delete(values);
if (fd >= 0)
{
g_file_close(fd);
}
return rv;
}
/*****************************************************************************/
/* Basic sanity checks before any forking */
static int
@ -295,15 +416,123 @@ xrdp_sanity_check(void)
return 0;
}
/*****************************************************************************/
static int
check_drop_privileges(struct xrdp_startup_params *startup_params)
{
int rv = 1;
const char *user = startup_params->runtime_user;
const char *group = startup_params->runtime_group;
if (user[0] == '\0' && group[0] == '\0')
{
// Allow this for now
LOG(LOG_LEVEL_ALWAYS,
"You are running xrdp as root. This is not safe.");
rv = 0;
}
else if (user[0] == '\0' || group[0] == '\0')
{
LOG(LOG_LEVEL_ERROR,
"Both a runtime_user and a runtime_group MUST be specified");
}
else
{
rv = g_drop_privileges(user, group);
if (rv == 0)
{
LOG(LOG_LEVEL_INFO, "Switched user:group to %s:%s", user, group);
}
}
return rv;
}
/*****************************************************************************/
static int
read_pid_file(const char *pid_file)
{
int pid = -1;
int fd = g_file_open_ro(pid_file); /* xrdp.pid */
if (fd >= 0)
{
char text[32];
g_memset(text, 0, sizeof(text));
g_file_read(fd, text, sizeof(text) - 1);
pid = g_atoi(text);
g_file_close(fd);
}
return pid;
}
/*****************************************************************************/
/**
* Kills an active xrdp daemon
*
* It is assumed that logging is not active
*
* @param pid_file PID file
* @return 0 for success
*/
static int
kill_daemon(const char *pid_file)
{
int status = 1;
int pid;
if (g_getuid() != 0)
{
g_writeln("Must be root");
}
else if ((pid = read_pid_file(pid_file)) > 0)
{
if (!g_pid_is_active(pid))
{
g_writeln("Daemon not active");
status = 0;
}
else
{
g_writeln("stopping process id %d", pid);
int i;
g_sigterm(pid);
g_sleep(100);
i = 5 * 1000 / 500;
while (i > 0 && g_pid_is_active(pid))
{
g_sleep(500);
--i;
}
if (g_pid_is_active(pid))
{
g_writeln("Can't stop process");
}
else
{
status = 0;
}
}
if (status == 0)
{
/* delete the xrdp.pid file, as xrdp can't do this
* if it's running without privilege */
g_file_delete(pid_file);
}
}
return status;
}
/*****************************************************************************/
int
main(int argc, char **argv)
{
int exit_status = 0;
int exit_status = 1;
enum logReturns error;
struct xrdp_startup_params startup_params = {0};
int pid;
int fd;
int daemon;
char text[256];
const char *pid_file = XRDP_PID_PATH "/xrdp.pid";
@ -368,36 +597,9 @@ main(int argc, char **argv)
if (startup_params.kill)
{
g_writeln("stopping xrdp");
/* read the xrdp.pid file */
fd = -1;
if (g_file_exist(pid_file)) /* xrdp.pid */
{
fd = g_file_open_ro(pid_file); /* xrdp.pid */
}
if (fd == -1)
{
g_writeln("cannot open %s, maybe xrdp is not running", pid_file);
}
else
{
g_memset(text, 0, 32);
g_file_read(fd, text, 31);
pid = g_atoi(text);
g_writeln("stopping process id %d", pid);
if (pid > 0)
{
g_sigterm(pid);
}
g_file_close(fd);
}
int status = kill_daemon(pid_file);
g_deinit();
g_exit(0);
g_exit(status);
}
/* starting logging subsystem */
@ -428,11 +630,17 @@ main(int argc, char **argv)
g_exit(1);
}
if (g_file_exist(pid_file)) /* xrdp.pid */
if (read_xrdp_ini_startup_params(&startup_params) != 0)
{
LOG(LOG_LEVEL_ALWAYS, "It looks like xrdp is already running.");
log_end();
g_deinit();
g_exit(1);
}
if ((pid = read_pid_file(pid_file)) > 0 && g_pid_is_active(pid))
{
LOG(LOG_LEVEL_ALWAYS,
"It looks like xrdp (pid=%d) is already running.", pid);
LOG(LOG_LEVEL_ALWAYS, "If not, delete %s and try again.", pid_file);
log_end();
g_deinit();
@ -444,14 +652,13 @@ main(int argc, char **argv)
if (daemon)
{
/* make sure containing directory exists */
g_create_path(pid_file);
/* make sure we can write to pid file */
fd = g_file_open_rw(pid_file); /* xrdp.pid */
int pid_fd = g_file_open_rw(pid_file); /* xrdp.pid */
if (fd == -1)
if (pid_fd == -1)
{
LOG(LOG_LEVEL_ALWAYS,
"running in daemon mode with no access to pid files, quitting");
@ -460,23 +667,14 @@ main(int argc, char **argv)
g_exit(1);
}
if (g_file_write(fd, "0", 1) == -1)
{
LOG(LOG_LEVEL_ALWAYS,
"running in daemon mode with no access to pid files, quitting");
log_end();
g_deinit();
g_exit(1);
}
/* Before daemonising, check we can listen.
* If we can't listen, exit with failure status */
struct xrdp_listen *xrdp_listen;
xrdp_listen = xrdp_listen_create(&startup_params);
int status = xrdp_listen_init(xrdp_listen);
xrdp_listen_delete(xrdp_listen);
g_file_close(fd);
g_file_delete(pid_file);
}
if (daemon)
{
/* if can't listen, exit with failure status */
if (xrdp_listen_test(&startup_params) != 0)
if (status != 0)
{
LOG(LOG_LEVEL_ALWAYS, "Failed to start xrdp daemon, "
"possibly address already in use.");
@ -486,6 +684,7 @@ main(int argc, char **argv)
or systemd cannot detect xrdp daemon couldn't start properly */
g_exit(1);
}
/* start of daemonizing code */
pid = g_fork();
@ -506,21 +705,10 @@ main(int argc, char **argv)
}
g_sleep(1000);
/* write the pid to file */
pid = g_getpid();
fd = g_file_open_rw(pid_file); /* xrdp.pid */
if (fd == -1)
{
LOG(LOG_LEVEL_WARNING, "Can't open %s for writing [%s]",
pid_file, g_get_strerror());
}
else
{
g_sprintf(text, "%d", pid);
g_file_write(fd, text, g_strlen(text));
g_file_close(fd);
}
/* write our pid to file */
g_sprintf(text, "%d", g_getpid());
g_file_write(pid_fd, text, g_strlen(text));
g_file_close(pid_fd);
g_sleep(1000);
g_file_close(0);
@ -542,43 +730,51 @@ main(int argc, char **argv)
/* end of daemonizing code */
}
g_set_threadid(tc_get_threadid());
g_listen = xrdp_listen_create();
g_signal_user_interrupt(xrdp_shutdown); /* SIGINT */
g_signal_pipe(xrdp_sig_no_op); /* SIGPIPE */
g_signal_terminate(xrdp_shutdown); /* SIGTERM */
g_signal_child_stop(xrdp_child); /* SIGCHLD */
g_signal_hang_up(xrdp_sig_no_op); /* SIGHUP */
g_set_sync_mutex(tc_mutex_create());
g_set_sync1_mutex(tc_mutex_create());
pid = g_getpid();
LOG(LOG_LEVEL_INFO, "starting xrdp with pid %d", pid);
g_snprintf(text, 255, "xrdp_%8.8x_main_term", pid);
g_set_term_event(g_create_wait_obj(text));
if (g_get_term() == 0)
g_listen = xrdp_listen_create(&startup_params);
if (xrdp_listen_init(g_listen) != 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_term_event");
LOG(LOG_LEVEL_ALWAYS, "Failed to start xrdp daemon, "
"possibly address already in use.");
}
else if (check_drop_privileges(&startup_params) == 0)
{
g_set_threadid(tc_get_threadid());
g_signal_user_interrupt(xrdp_shutdown); /* SIGINT */
g_signal_pipe(xrdp_sig_no_op); /* SIGPIPE */
g_signal_terminate(xrdp_shutdown); /* SIGTERM */
g_signal_child_stop(xrdp_child); /* SIGCHLD */
g_signal_hang_up(xrdp_sig_no_op); /* SIGHUP */
g_set_sync_mutex(tc_mutex_create());
g_set_sync1_mutex(tc_mutex_create());
pid = g_getpid();
LOG(LOG_LEVEL_INFO, "starting xrdp with pid %d", pid);
g_snprintf(text, 255, "xrdp_%8.8x_main_term", pid);
g_set_term_event(g_create_wait_obj(text));
if (g_get_term() == 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_term_event");
}
g_snprintf(text, 255, "xrdp_%8.8x_main_sigchld", pid);
g_set_sigchld_event(g_create_wait_obj(text));
if (g_get_sigchld() == 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_sigchld_event");
}
g_snprintf(text, 255, "xrdp_%8.8x_main_sync", pid);
g_set_sync_event(g_create_wait_obj(text));
if (g_get_sync_event() == 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_sync_event");
}
exit_status = xrdp_listen_main_loop(g_listen);
}
g_snprintf(text, 255, "xrdp_%8.8x_main_sigchld", pid);
g_set_sigchld_event(g_create_wait_obj(text));
if (g_get_sigchld() == 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_sigchld_event");
}
g_snprintf(text, 255, "xrdp_%8.8x_main_sync", pid);
g_set_sync_event(g_create_wait_obj(text));
if (g_get_sync_event() == 0)
{
LOG(LOG_LEVEL_WARNING, "error creating g_sync_event");
}
g_listen->startup_params = &startup_params;
exit_status = xrdp_listen_main_loop(g_listen);
xrdp_listen_delete(g_listen);
tc_mutex_delete(g_get_sync_mutex());
@ -596,10 +792,10 @@ main(int argc, char **argv)
g_delete_wait_obj(g_get_sync_event());
g_set_sync_event(0);
/* only main process should delete pid file */
if (daemon && (pid == g_getpid()))
if (daemon)
{
/* delete the xrdp.pid file */
/* Try to delete the PID file, although if we've dropped
* privileges this won't be successful */
g_file_delete(pid_file);
}

View File

@ -194,13 +194,14 @@ xrdp_process_main_loop(struct xrdp_process *self);
/* xrdp_listen.c */
struct xrdp_listen *
xrdp_listen_create(void);
xrdp_listen_create(struct xrdp_startup_params *startup_params);
int
xrdp_listen_init(struct xrdp_listen *self);
void
xrdp_listen_delete(struct xrdp_listen *self);
int
xrdp_listen_main_loop(struct xrdp_listen *self);
int
xrdp_listen_test(struct xrdp_startup_params *startup_params);
/* xrdp_region.c */
struct xrdp_region *

View File

@ -27,6 +27,12 @@ port=3389
; prefer use vsock://<cid>:<port> above
use_vsock=false
; Unprivileged User name and group to run the xrdp daemon.
; It is HIGHLY RECOMMENDED you set these values. See the xrdp.ini(5)
; manpage for more information on setting and checking these.
#runtime_user=xrdp
#runtime_group=xrdp
; regulate if the listening socket use socket option tcp_nodelay
; no buffering will be performed in the TCP stack
tcp_nodelay=true

View File

@ -55,7 +55,7 @@ xrdp_listen_create_pro_done(struct xrdp_listen *self)
/*****************************************************************************/
struct xrdp_listen *
xrdp_listen_create(void)
xrdp_listen_create(struct xrdp_startup_params *startup_params)
{
struct xrdp_listen *self;
@ -64,6 +64,7 @@ xrdp_listen_create(void)
self->trans_list = list_create();
self->process_list = list_create();
self->fork_list = list_create();
self->startup_params = startup_params;
if (g_process_sem == 0)
{
@ -153,95 +154,6 @@ xrdp_process_run(void *in_val)
LOG_DEVEL(LOG_LEVEL_TRACE, "process done");
return 0;
}
/*****************************************************************************/
static int
xrdp_listen_get_startup_params(struct xrdp_listen *self)
{
int fd;
int index;
int port_override;
int fork_override;
char *val;
struct list *names;
struct list *values;
struct xrdp_startup_params *startup_params;
startup_params = self->startup_params;
port_override = startup_params->port[0] != 0;
fork_override = startup_params->fork;
fd = g_file_open_ro(startup_params->xrdp_ini);
if (fd != -1)
{
names = list_create();
names->auto_free = 1;
values = list_create();
values->auto_free = 1;
if (file_read_section(fd, "globals", names, values) == 0)
{
for (index = 0; index < names->count; index++)
{
val = (char *)list_get_item(names, index);
if (val != 0)
{
if (g_strcasecmp(val, "port") == 0)
{
if (port_override == 0)
{
val = (char *) list_get_item(values, index);
g_strncpy(startup_params->port, val,
sizeof(startup_params->port) - 1);
}
}
if (g_strcasecmp(val, "fork") == 0)
{
if (fork_override == 0)
{
val = (char *) list_get_item(values, index);
startup_params->fork = g_text2bool(val);
}
}
if (g_strcasecmp(val, "tcp_nodelay") == 0)
{
val = (char *)list_get_item(values, index);
startup_params->tcp_nodelay = g_text2bool(val);
}
if (g_strcasecmp(val, "tcp_keepalive") == 0)
{
val = (char *)list_get_item(values, index);
startup_params->tcp_keepalive = g_text2bool(val);
}
if (g_strcasecmp(val, "tcp_send_buffer_bytes") == 0)
{
val = (char *)list_get_item(values, index);
startup_params->tcp_send_buffer_bytes = g_atoi(val);
}
if (g_strcasecmp(val, "tcp_recv_buffer_bytes") == 0)
{
val = (char *)list_get_item(values, index);
startup_params->tcp_recv_buffer_bytes = g_atoi(val);
}
if (g_strcasecmp(val, "use_vsock") == 0)
{
val = (char *)list_get_item(values, index);
startup_params->use_vsock = g_text2bool(val);
}
}
}
}
list_delete(names);
list_delete(values);
g_file_close(fd);
}
return 0;
}
/*****************************************************************************/
static int
xrdp_listen_stop_all_listen(struct xrdp_listen *self)
@ -651,8 +563,10 @@ xrdp_listen_pp(struct xrdp_listen *self, int *index,
}
/*****************************************************************************/
static int
xrdp_listen_process_startup_params(struct xrdp_listen *self)
/* returns 0 if xrdp is listening correctly
returns 1 if xrdp is not listening correctly */
int
xrdp_listen_init(struct xrdp_listen *self)
{
int mode; /* TRANS_MODE_TCP*, TRANS_MODE_UNIX, TRANS_MODE_VSOCK */
int error;
@ -886,18 +800,7 @@ xrdp_listen_main_loop(struct xrdp_listen *self)
struct trans *ltrans;
self->status = 1;
if (xrdp_listen_get_startup_params(self) != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_listen_main_loop: xrdp_listen_get_port failed");
self->status = -1;
return 1;
}
if (xrdp_listen_process_startup_params(self) != 0)
{
LOG(LOG_LEVEL_ERROR, "xrdp_listen_main_loop: xrdp_listen_get_port failed");
self->status = -1;
return 1;
}
term_obj = g_get_term(); /*Global termination event */
sigchld_obj = g_get_sigchld();
sync_obj = g_get_sync_event();
@ -1037,27 +940,3 @@ xrdp_listen_main_loop(struct xrdp_listen *self)
self->status = -1;
return 0;
}
/*****************************************************************************/
/* returns 0 if xrdp can listen
returns 1 if xrdp cannot listen */
int
xrdp_listen_test(struct xrdp_startup_params *startup_params)
{
struct xrdp_listen *xrdp_listen;
xrdp_listen = xrdp_listen_create();
xrdp_listen->startup_params = startup_params;
if (xrdp_listen_get_startup_params(xrdp_listen) != 0)
{
xrdp_listen_delete(xrdp_listen);
return 1;
}
if (xrdp_listen_process_startup_params(xrdp_listen) != 0)
{
xrdp_listen_delete(xrdp_listen);
return 1;
}
xrdp_listen_delete(xrdp_listen);
return 0;
}

View File

@ -750,6 +750,10 @@ struct xrdp_startup_params
int tcp_nodelay;
int tcp_keepalive;
int use_vsock;
// These should be local users/groups, and so we shouldn't need
// a lot of storage for them.
char runtime_user[64];
char runtime_group[64];
};
/*