Skip to content

Commit

Permalink
Add xrdp-chkpriv script to check xrdp privileges
Browse files Browse the repository at this point in the history
  • Loading branch information
matt335672 committed Mar 1, 2024
1 parent 4471012 commit 2fa9520
Show file tree
Hide file tree
Showing 7 changed files with 301 additions and 5 deletions.
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,7 @@ AC_CONFIG_FILES([
tools/Makefile
tools/devel/Makefile
tools/devel/tcp_proxy/Makefile
tools/chkpriv/Makefile
vnc/Makefile
xrdpapi/Makefile
xrdp/Makefile
Expand Down
12 changes: 12 additions & 0 deletions docs/man/xrdp.ini.5.in
Original file line number Diff line number Diff line change
Expand Up @@ -127,9 +127,21 @@ 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
Expand Down
1 change: 1 addition & 0 deletions tools/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

SUBDIRS = \
chkpriv \
devel
31 changes: 31 additions & 0 deletions tools/chkpriv/Makefile.am
Original file line number Diff line number Diff line change
@@ -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

205 changes: 205 additions & 0 deletions tools/chkpriv/xrdp-chkpriv.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#!/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 ownbership"
else
echo "-Error- xrdp.ini and sesman.ini do not agree on group ownbership"
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 by the user?
#
# These aren't necessarily our files, so we can't be 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
if ! [ -e $certificate ]; then
echo "-Error- $certificate does not exist"
errors=$(( errors + 1 ))
elif $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -r '"$certificate"' ]'
then
echo "-Info- $certificate is readable by $runtime_user:$runtime_group"
else
echo "-Error- $certificate is not readable by $runtime_user:$runtime_group"
errors=$(( errors + 1 ))
fi

if ! [ -e $key_file ]; then
echo "-Error- $key_file does not exist"
errors=$(( errors + 1 ))
elif $DROPPRIV "$runtime_user" "$runtime_group" sh -c '[ -r '"$key_file"' ]'
sh -c '[ -r '"$key_file"' ]'
then
echo "-Info- $key_file is readable by $runtime_user:$runtime_group"
else
echo "-Error- $key_file is not readable by $runtime_user:$runtime_group"
errors=$(( errors + 1 ))
fi

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 changes: 49 additions & 0 deletions tools/chkpriv/xrdp-droppriv.c
Original file line number Diff line number Diff line change
@@ -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;
}
7 changes: 2 additions & 5 deletions xrdp/xrdp.ini.in
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,8 @@ port=3389
use_vsock=false

; Unprivileged User name and group to run the xrdp daemon.
; It is HIGHLY RECOMMENDED you set these values.
; A suitable user and group can be added with a command like this (Linux):-
; useradd xrdp -d / -c 'xrdp daemon' -s /usr/sbin/nologin
; Be aware that runtime_group here, and SessionSockdirGroup in sesman.ini
; MUST be the same if you want to run sessions.
; 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

Expand Down

0 comments on commit 2fa9520

Please sign in to comment.