From 12c46664a21bdfac77e14d78ee4778cb30fdb76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mikrut?= Date: Fri, 20 Mar 2020 11:21:34 +0100 Subject: [PATCH 01/29] Small fixes found by static analyzers. --- common/log.h | 2 +- genkeymap/genkeymap.c | 2 +- xrdp/xrdp_wm.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/log.h b/common/log.h index 2f8063e1..7545b1f0 100644 --- a/common/log.h +++ b/common/log.h @@ -114,7 +114,7 @@ internal_log_lvl2str(const enum logLevels lvl, char *str); * */ enum logLevels -internal_log_text2level(const char *s); +internal_log_text2level(const char *buf); /** * A function that init our struct that holds all state and diff --git a/genkeymap/genkeymap.c b/genkeymap/genkeymap.c index 03e54d8c..3b654454 100644 --- a/genkeymap/genkeymap.c +++ b/genkeymap/genkeymap.c @@ -136,7 +136,7 @@ int main(int argc, char **argv) fprintf(outf, "[%s]\n", sections[idx]); e.state = states[idx]; - for (i = 8; i <= 137; i++) /* Keycodes */ + for (i = 8; i < 137; i++) /* Keycodes */ { if (is_evdev) e.keycode = xfree86_to_evdev[i-8]; diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c index 1f44f639..e9bc46b2 100644 --- a/xrdp/xrdp_wm.c +++ b/xrdp/xrdp_wm.c @@ -913,7 +913,7 @@ xrdp_wm_xor_pat(struct xrdp_wm *self, int x, int y, int cx, int cy) self->painter->brush.pattern[6] = 0xaa; self->painter->brush.pattern[7] = 0x55; self->painter->brush.x_origin = 0; - self->painter->brush.x_origin = 0; + self->painter->brush.y_origin = 0; self->painter->brush.style = 3; self->painter->bg_color = self->black; self->painter->fg_color = self->white; From 1adb3c7b3326120dc1aa912c881f16815f641255 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Thu, 2 Apr 2020 16:18:06 +0900 Subject: [PATCH 02/29] Fix Xorg path for Arch Linux It has been moved: https://www.archlinux.org/packages/extra/x86_64/xorg-server/ Fixes: #1448 --- sesman/sesman.ini.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 483aac0e..869f5bb5 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -80,7 +80,7 @@ SyslogLevel=DEBUG ; Fedora 26 or later : param=/usr/libexec/Xorg ; Debian 9 or later : param=/usr/lib/xorg/Xorg ; Ubuntu 16.04 or later : param=/usr/lib/xorg/Xorg -; Arch Linux : param=/usr/lib/xorg-server/Xorg +; Arch Linux : param=/usr/lib/Xorg ; CentOS 7 : param=/usr/bin/Xorg or param=Xorg ; param=Xorg From f5164e8ff730758660ba1a52d7411692dd8463c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?okhowang=28=E7=8E=8B=E6=B2=9B=E6=96=87=29?= Date: Mon, 16 Mar 2020 23:08:39 +0800 Subject: [PATCH 03/29] workaround odd shift behaviour --- xrdp/xrdp_wm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c index 1f44f639..90b7ce15 100644 --- a/xrdp/xrdp_wm.c +++ b/xrdp/xrdp_wm.c @@ -1532,6 +1532,12 @@ xrdp_wm_key(struct xrdp_wm *self, int device_flags, int scan_code) return 0; } + // workaround odd shift behavior + // see https://github.com/neutrinolabs/xrdp/issues/397 + if (scan_code == 42 && device_flags == (KBD_FLAG_UP | KBD_FLAG_EXT)) { + return 0; + } + if (device_flags & KBD_FLAG_UP) /* 0x8000 */ { self->keys[scan_code] = 0; From da3114007cc22d7fc8595beeb21fc230e33e9cc2 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 14 Apr 2020 11:01:52 +0100 Subject: [PATCH 04/29] Address possible memory out-of-bounds accesses --- common/trans.c | 9 ++-- libxrdp/libxrdp.c | 14 ++--- libxrdp/xrdp_iso.c | 129 ++++++++++++++++++++++++++++++--------------- 3 files changed, 94 insertions(+), 58 deletions(-) diff --git a/common/trans.c b/common/trans.c index 883d7cbd..27efa8f8 100644 --- a/common/trans.c +++ b/common/trans.c @@ -452,17 +452,14 @@ trans_force_read_s(struct trans *self, struct stream *in_s, int size) { int rcvd; - if (self->status != TRANS_STATUS_UP) + if (self->status != TRANS_STATUS_UP || + size < 0 || !s_check_rem_out(in_s, size)) { return 1; } + while (size > 0) { - /* make sure stream has room */ - if ((in_s->end + size) > (in_s->data + in_s->size)) - { - return 1; - } rcvd = self->trans_recv(self, in_s->end, size); if (rcvd == -1) { diff --git a/libxrdp/libxrdp.c b/libxrdp/libxrdp.c index 27e2c3b7..0160bcc8 100644 --- a/libxrdp/libxrdp.c +++ b/libxrdp/libxrdp.c @@ -125,24 +125,18 @@ libxrdp_force_read(struct trans* trans) if (trans_force_read(trans, 4) != 0) { - g_writeln("libxrdp_force_read: error"); + g_writeln("libxrdp_force_read: header read error"); return 0; } bytes = libxrdp_get_pdu_bytes(s->data); - if (bytes < 1) + if (bytes < 4 || bytes > s->size) { - g_writeln("libxrdp_force_read: error"); + g_writeln("libxrdp_force_read: bad header length %d", bytes); return 0; } - if (bytes > 32 * 1024) - { - g_writeln("libxrdp_force_read: error"); - return 0; - } - if (trans_force_read(trans, bytes - 4) != 0) { - g_writeln("libxrdp_force_read: error"); + g_writeln("libxrdp_force_read: Can't read PDU"); return 0; } return s; diff --git a/libxrdp/xrdp_iso.c b/libxrdp/xrdp_iso.c index a53af5ce..27d5a57a 100644 --- a/libxrdp/xrdp_iso.c +++ b/libxrdp/xrdp_iso.c @@ -129,6 +129,12 @@ xrdp_iso_process_rdp_neg_req(struct xrdp_iso *self, struct stream *s) int flags; int len; + if (!s_check_rem(s, 7)) + { + LLOGLN(10, ("xrdp_iso_process_rdpNegReq: unexpected end-of-record")); + return 1; + } + in_uint8(s, flags); if (flags != 0x0 && flags != 0x8 && flags != 0x1) { @@ -153,13 +159,22 @@ xrdp_iso_process_rdp_neg_req(struct xrdp_iso *self, struct stream *s) return 0; } -/*****************************************************************************/ -/* returns error */ +/***************************************************************************** + * Reads an X.224 PDU (X.224 section 13) preceded by a T.123 TPKT + * header (T.123 section 8) + * + * On entry, the TPKT header length field will have been inspected and used to + * set up the input stream. + * + * On exit, the TPKT header and the fixed part of the PDU header will have been + * removed from the stream. + * + * Returns error + *****************************************************************************/ static int xrdp_iso_recv_msg(struct xrdp_iso *self, struct stream *s, int *code, int *len) { int ver; - int plen; *code = 0; *len = 0; @@ -169,7 +184,16 @@ xrdp_iso_recv_msg(struct xrdp_iso *self, struct stream *s, int *code, int *len) LLOGLN(10, ("xrdp_iso_recv_msg error logic")); } + /* TPKT header is 4 bytes, then first 2 bytes of the X.224 CR-TPDU */ + if (!s_check_rem(s, 6)) + { + return 1; + } + in_uint8(s, ver); + in_uint8s(s, 3); /* Skip reserved field, plus length */ + in_uint8(s, *len); + in_uint8(s, *code); if (ver != 3) { @@ -178,24 +202,17 @@ xrdp_iso_recv_msg(struct xrdp_iso *self, struct stream *s, int *code, int *len) return 1; } - in_uint8s(s, 1); - in_uint16_be(s, plen); - - if (plen < 4) + if (*len == 255) { + /* X.224 13.2.1 - reserved value */ + LLOGLN(10, ("xrdp_iso_recv_msg: reserved length encountered")); + LHEXDUMP(10, (s->data, 4)); return 1; } - if (!s_check_rem(s, 2)) - { - return 1; - } - - in_uint8(s, *len); - in_uint8(s, *code); - if (*code == ISO_PDU_DT) { + /* Data PDU : X.224 13.7 */ if (!s_check_rem(s, 1)) { return 1; @@ -204,6 +221,7 @@ xrdp_iso_recv_msg(struct xrdp_iso *self, struct stream *s, int *code, int *len) } else { + /* Other supported PDUs : X.224 13.x */ if (!s_check_rem(s, 5)) { return 1; @@ -302,7 +320,20 @@ xrdp_iso_send_cc(struct xrdp_iso *self) free_stream(s); return 0; } -/*****************************************************************************/ +/***************************************************************************** + * Process an X.224 connection request PDU + * + * See MS-RDPCGR v20190923 sections 2.2.1.1 and 3.3.5.3.1. + * + * From the latter, in particular:- + * - The length embedded in the TPKT header MUST be examined for + * consistency with the received data. If there is a discrepancy, the + * connection SHOULD be dropped + * - If the optional routingToken field exists it MUST be ignored. + * - If the optional cookie field is present it MUST be ignored. + * - If both the routingToken and cookie fields are present, the server + * SHOULD continue with the connection. + *****************************************************************************/ /* returns error */ int xrdp_iso_incoming(struct xrdp_iso *self) @@ -310,16 +341,14 @@ xrdp_iso_incoming(struct xrdp_iso *self) int rv = 0; int code; int len; - int cookie_index; int cc_type; - char text[256]; - char *pend; struct stream *s; + int expected_pdu_len; LLOGLN(10, (" in xrdp_iso_incoming")); s = libxrdp_force_read(self->trans); - if (s == 0) + if (s == NULL) { return 1; } @@ -330,15 +359,29 @@ xrdp_iso_incoming(struct xrdp_iso *self) return 1; } - if ((code != ISO_PDU_CR) || (len < 6)) + if (code != ISO_PDU_CR) { return 1; } + /* + * Make sure the length indicator field extracted from the X.224 + * connection request TPDU corresponds to the length in the TPKT header. + * + * We do this by seeing how the indicator field minus the counted + * octets in the TPDU header (6) compares with the space left in + * the stream. + */ + expected_pdu_len = (s->end - s->p) + 6; + if (len != expected_pdu_len) + { + LLOGLN(0, ("xrdp_iso_incoming: X.224 CR-TPDU length exp %d got %d", + expected_pdu_len, len)); + return 1; + } + /* process connection request */ - pend = s->p + (len - 6); - cookie_index = 0; - while (s->p < pend) + while (s_check_rem(s, 1)) { in_uint8(s, cc_type); switch (cc_type) @@ -355,28 +398,30 @@ xrdp_iso_incoming(struct xrdp_iso *self) break; case RDP_CORRELATION_INFO: /* rdpCorrelationInfo 6 */ // TODO + if (!s_check_rem(s, 1 + 2 + 16 + 16)) + { + LLOGLN(0, ("xrdp_iso_incoming: short correlation info")); + return 1; + } + in_uint8s(s, 1 + 2 + 16 + 16); break; - case 'C': /* Cookie routingToken */ - while (s->p < pend) + case 'C': /* Cookie */ + /* The routingToken and cookie fields are both ASCII + * strings starting with the word 'Cookie: ' and + * ending with CR+LF. We ignore both, so we do + * not need to distinguish them */ + while (s_check_rem(s, 1)) { - text[cookie_index] = cc_type; - cookie_index++; - if (cookie_index > 255) - { - cookie_index = 255; - } - if ((s->p[0] == 0x0D) && (s->p[1] == 0x0A)) - { - in_uint8s(s, 2); - text[cookie_index] = 0; - cookie_index = 0; - if (g_strlen(text) > 0) - { - } - break; - } in_uint8(s, cc_type); + if (cc_type == 0x0D && s_check_rem(s, 1)) + { + in_uint8(s, cc_type); + if (cc_type == 0x0A) + { + break; + } + } } break; } From 7ef01f7b0cec6e75631a22906d67d8ff44cb1a8c Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Sat, 18 Apr 2020 16:56:53 +0100 Subject: [PATCH 05/29] Address memory allocation overflow security issues --- sesman/chansrv/chansrv.c | 12 ++++++++- sesman/chansrv/smartcard_pcsc.c | 22 ++++++++++++++--- xrdp/xrdp_bitmap.c | 44 +++++++++++++++++++++++++++++++-- 3 files changed, 71 insertions(+), 7 deletions(-) diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index 6b1b9326..d21d5b70 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -39,6 +39,9 @@ #include "xrdp_sockets.h" #include "audin.h" +#define CHANNEL_NAME_BYTES 7 +#define MAX_PATH 260 + static struct trans *g_lis_trans = 0; static struct trans *g_con_trans = 0; static struct trans *g_api_lis_trans = 0; @@ -1042,7 +1045,7 @@ my_api_trans_data_in(struct trans *trans) int rv; int bytes; int ver; - int channel_name_bytes; + unsigned int channel_name_bytes; struct chansrv_drdynvc_procs procs; char *chan_name; @@ -1067,6 +1070,13 @@ my_api_trans_data_in(struct trans *trans) rv = 1; in_uint32_le(s, channel_name_bytes); //g_writeln("my_api_trans_data_in: channel_name_bytes %d", channel_name_bytes); + /* + * Name is limited to CHANNEL_NAME_BYTES for an SVC, or MAX_PATH + * bytes for a DVC */ + if (channel_name_bytes > MAX(CHANNEL_NAME_BYTES, MAX_PATH)) + { + return 1; + } chan_name = g_new0(char, channel_name_bytes + 1); if (chan_name == NULL) { diff --git a/sesman/chansrv/smartcard_pcsc.c b/sesman/chansrv/smartcard_pcsc.c index 51409567..5f4516db 100644 --- a/sesman/chansrv/smartcard_pcsc.c +++ b/sesman/chansrv/smartcard_pcsc.c @@ -586,9 +586,18 @@ int scard_process_list_readers(struct trans *con, struct stream *in_s) { int hContext; - int bytes_groups; + unsigned int bytes_groups; int cchReaders; - char *groups; + /* + * At the time of writing, the groups strings which can be sent + * over this interface are all small:- + * + * "SCard$AllReaders", "SCard$DefaultReaders", "SCard$LocalReaders" and + * "SCard$SystemReaders" + * + * We'll allow a bit extra in case the interface changes + */ + char groups[256]; struct pcsc_uds_client *uds_client; struct pcsc_context *lcontext; struct pcsc_list_readers *pcscListReaders; @@ -597,8 +606,14 @@ scard_process_list_readers(struct trans *con, struct stream *in_s) uds_client = (struct pcsc_uds_client *) (con->callback_data); in_uint32_le(in_s, hContext); in_uint32_le(in_s, bytes_groups); - groups = (char *) g_malloc(bytes_groups + 1, 1); + if (bytes_groups > (sizeof(groups) - 1)) + { + LLOGLN(0, ("scard_process_list_readers: Unreasonable string length %u", + bytes_groups)); + return 1; + } in_uint8a(in_s, groups, bytes_groups); + groups[bytes_groups] = '\0'; in_uint32_le(in_s, cchReaders); LLOGLN(10, ("scard_process_list_readers: hContext 0x%8.8x cchReaders %d", hContext, cchReaders)); @@ -615,7 +630,6 @@ scard_process_list_readers(struct trans *con, struct stream *in_s) pcscListReaders->cchReaders = cchReaders; scard_send_list_readers(pcscListReaders, lcontext->context, lcontext->context_bytes, groups, cchReaders, 1); - g_free(groups); return 0; } diff --git a/xrdp/xrdp_bitmap.c b/xrdp/xrdp_bitmap.c index 1f5d010f..627f73c7 100644 --- a/xrdp/xrdp_bitmap.c +++ b/xrdp/xrdp_bitmap.c @@ -25,6 +25,8 @@ #include #endif +#include + #include "xrdp.h" #include "log.h" @@ -93,6 +95,30 @@ static const unsigned int g_crc_table[256] = (in_crc) = g_crc_table[((in_crc) ^ (in_pixel)) & 0xff] ^ ((in_crc) >> 8) #define CRC_END(in_crc) (in_crc) = ((in_crc) ^ 0xFFFFFFFF) +/*****************************************************************************/ +/* Allocate bitmap for specified dimensions, checking for int overflow */ +static char * +alloc_bitmap_data(int width, int height, int Bpp) +{ + char *result = NULL; + if (width > 0 && height > 0 && Bpp > 0) + { + int len = width; + /* g_malloc() currently takes an 'int' size */ + if (len < INT_MAX / height) + { + len *= height; + if (len < INT_MAX / Bpp) + { + len *= Bpp; + result = (char *)malloc(len); + } + } + } + + return result; +} + /*****************************************************************************/ struct xrdp_bitmap * xrdp_bitmap_create(int width, int height, int bpp, @@ -123,14 +149,28 @@ xrdp_bitmap_create(int width, int height, int bpp, if (self->type == WND_TYPE_BITMAP || self->type == WND_TYPE_IMAGE) { - self->data = (char *)g_malloc(width * height * Bpp, 0); + self->data = alloc_bitmap_data(width, height, Bpp); + if (self->data == NULL) + { + LLOGLN(0, ("xrdp_bitmap_create: size overflow %dx%dx%d", + width, height, Bpp)); + g_free(self); + return NULL; + } } #if defined(XRDP_PAINTER) if (self->type == WND_TYPE_SCREEN) /* noorders */ { LLOGLN(0, ("xrdp_bitmap_create: noorders")); - self->data = (char *) g_malloc(width * height * Bpp, 0); + self->data = alloc_bitmap_data(width, height, Bpp); + if (self->data == NULL) + { + LLOGLN(0, ("xrdp_bitmap_create: size overflow %dx%dx%d", + width, height, Bpp)); + g_free(self); + return NULL; + } } #endif From 617283eb3448cb482fef3bb79b9a5e84dfea3a9b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:27:36 +0100 Subject: [PATCH 06/29] Remove unnecessary g_malloc() call --- sesman/chansrv/chansrv.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index d21d5b70..c154f684 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -1045,9 +1045,13 @@ my_api_trans_data_in(struct trans *trans) int rv; int bytes; int ver; - unsigned int channel_name_bytes; struct chansrv_drdynvc_procs procs; - char *chan_name; + /* + * Name is limited to CHANNEL_NAME_BYTES for an SVC, or MAX_PATH + * bytes for a DVC + */ + char chan_name[MAX(CHANNEL_NAME_BYTES, MAX_PATH) + 1]; + unsigned int channel_name_bytes; //g_writeln("my_api_trans_data_in: extra_flags %d", trans->extra_flags); rv = 0; @@ -1070,19 +1074,13 @@ my_api_trans_data_in(struct trans *trans) rv = 1; in_uint32_le(s, channel_name_bytes); //g_writeln("my_api_trans_data_in: channel_name_bytes %d", channel_name_bytes); - /* - * Name is limited to CHANNEL_NAME_BYTES for an SVC, or MAX_PATH - * bytes for a DVC */ - if (channel_name_bytes > MAX(CHANNEL_NAME_BYTES, MAX_PATH)) - { - return 1; - } - chan_name = g_new0(char, channel_name_bytes + 1); - if (chan_name == NULL) + if (channel_name_bytes > (sizeof(chan_name) - 1)) { return 1; } in_uint8a(s, chan_name, channel_name_bytes); + chan_name[channel_name_bytes] = '\0'; + in_uint32_le(s, ad->chan_flags); //g_writeln("my_api_trans_data_in: chan_name %s chan_flags 0x%8.8x", chan_name, ad->chan_flags); if (ad->chan_flags == 0) @@ -1142,7 +1140,6 @@ my_api_trans_data_in(struct trans *trans) // "chan_id %d", rv, ad->chan_id); g_drdynvcs[ad->chan_id].xrdp_api_trans = trans; } - g_free(chan_name); init_stream(s, 0); trans->extra_flags = 2; trans->header_size = 0; From 2ec97206120de3a64874ec78f38e7d23285d56bc Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Sun, 26 Apr 2020 17:27:19 +0100 Subject: [PATCH 07/29] mkpamrules now supports Slackware --- instfiles/pam.d/mkpamrules | 80 ++++++++++++++++++++++-------- instfiles/pam.d/xrdp-sesman.system | 5 ++ instfiles/pam.d/xrdp-sesman.unix | 19 +++++-- 3 files changed, 78 insertions(+), 26 deletions(-) create mode 100644 instfiles/pam.d/xrdp-sesman.system diff --git a/instfiles/pam.d/mkpamrules b/instfiles/pam.d/mkpamrules index d90ad54b..a75bfc32 100755 --- a/instfiles/pam.d/mkpamrules +++ b/instfiles/pam.d/mkpamrules @@ -10,51 +10,87 @@ service="xrdp-sesman" pamdir="/etc/pam.d" pamdir_suse="/usr/etc/pam.d" +# Modules needed by xrdp-sesman.unix, if we get to that +unix_modules_needed="pam_unix.so pam_env.so pam_nologin.so" + +# Directories where pam modules might be installed +# Add to this list as platforms are added +pam_module_dir_searchpath="/lib*/security /usr/lib*/security /lib/*/security /usr/lib/*/security" + +find_pam_module_dir() +{ + # Looks for the pam security module directory + set -- $pam_module_dir_searchpath + for d in "$@"; do + if [ -s $d/pam_unix.so ]; then + echo $d + break + fi + done +} + +can_apply_unix_config() +{ + result=0 + module_dir="$1" + for m in $unix_modules_needed; do + if [ ! -s $module_dir/$m ]; then + echo " ** $m not found" >&2 + result=1 + fi + done + + return $result +} + guess_rules () { - if test -s "$pamdir/password-auth"; then + rules= + if [ -s "$pamdir/password-auth" ]; then rules="redhat" - return - fi - if test -s "$pamdir_suse/common-account"; then + elif [ -s "$pamdir_suse/common-account" ]; then rules="suse" - return - fi - if test -s "$pamdir/common-account"; then + elif [ -s "$pamdir/common-account" ]; then if grep "^@include" "$pamdir/passwd" >/dev/null 2>&1; then rules="debian" else rules="suse" fi - return - fi - if test ! -f "$pamdir/system-auth" -a -s "$pamdir/system"; then + elif [ ! -f "$pamdir/system-auth" -a -s "$pamdir/system" ]; then rules="freebsd" - return - fi - if test -s "$pamdir/authorization"; then + elif [ -s "$pamdir/authorization" ]; then rules="macos" - return - fi - if test -s "$pamdir/system-remote-login"; then + elif [ -s "$pamdir/system-remote-login" ]; then rules="arch" - return - fi - rules="unix" - return + elif [ -s "$pamdir/system-auth" ]; then + rules="system" + + else + module_dir=`find_pam_module_dir` + if [ -d "$module_dir" ]; then + #echo "- Found pam modules in $module_dir" >&2 + if can_apply_unix_config "$module_dir" ; then + rules="unix" + fi + fi + fi } -if test "$rules" = "auto"; then +if [ "$rules" = "auto" ]; then guess_rules + if [ -z "$rules" ]; then + echo "** Can't guess PAM rules for this system" + exit 1 + fi fi -if test -s "$srcdir/$service.$rules"; then +if [ -s "$srcdir/$service.$rules" ]; then ln -nsf "$srcdir/$service.$rules" "$outfile" else echo "Cannot find $srcdir/$service.$rules" diff --git a/instfiles/pam.d/xrdp-sesman.system b/instfiles/pam.d/xrdp-sesman.system new file mode 100644 index 00000000..5025a0f9 --- /dev/null +++ b/instfiles/pam.d/xrdp-sesman.system @@ -0,0 +1,5 @@ +#%PAM-1.0 +auth include system-auth +account include system-auth +password include system-auth +session include system-auth diff --git a/instfiles/pam.d/xrdp-sesman.unix b/instfiles/pam.d/xrdp-sesman.unix index 5025a0f9..3bee185c 100644 --- a/instfiles/pam.d/xrdp-sesman.unix +++ b/instfiles/pam.d/xrdp-sesman.unix @@ -1,5 +1,16 @@ #%PAM-1.0 -auth include system-auth -account include system-auth -password include system-auth -session include system-auth +# +# Really basic authentication set when nothing else is available +# +# You may need to edit this to suit your system depending on the +# required functionality. +# +auth required pam_unix.so shadow +auth required pam_env.so + +password required pam_unix.so + +account required pam_unix.so +account required pam_nologin.so + +session required pam_unix.so From aa0dbbae15cbfe9c891136b2757c6dc74eb8353b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 27 Apr 2020 15:01:56 +0100 Subject: [PATCH 08/29] Added CHANNEL_NAME_LEN to ms-rdpbcgr.h --- common/ms-rdpbcgr.h | 7 +++++-- sesman/chansrv/chansrv.c | 7 ++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/common/ms-rdpbcgr.h b/common/ms-rdpbcgr.h index ad81d0df..03e03c39 100644 --- a/common/ms-rdpbcgr.h +++ b/common/ms-rdpbcgr.h @@ -74,8 +74,11 @@ #define CONNECTION_TYPE_LAN 0x06 #define CONNECTION_TYPE_AUTODETECT 0x07 -/* Virtual channel options */ -/* Channel Definition Structure: options (2.2.1.3.4.1) */ +/* Channel definition structure CHANNEL_DEF (2.2.1.3.4.1) */ +/* This isn't explicitly named in MS-RDPBCGR */ +#define CHANNEL_NAME_LEN 7 + +/* Oprions field */ /* NOTE: XR_ prefixed to avoid conflict with FreeRDP */ #define XR_CHANNEL_OPTION_INITIALIZED 0x80000000 #define XR_CHANNEL_OPTION_ENCRYPT_RDP 0x40000000 diff --git a/sesman/chansrv/chansrv.c b/sesman/chansrv/chansrv.c index c154f684..d7ef1869 100644 --- a/sesman/chansrv/chansrv.c +++ b/sesman/chansrv/chansrv.c @@ -39,7 +39,8 @@ #include "xrdp_sockets.h" #include "audin.h" -#define CHANNEL_NAME_BYTES 7 +#include "ms-rdpbcgr.h" + #define MAX_PATH 260 static struct trans *g_lis_trans = 0; @@ -1047,10 +1048,10 @@ my_api_trans_data_in(struct trans *trans) int ver; struct chansrv_drdynvc_procs procs; /* - * Name is limited to CHANNEL_NAME_BYTES for an SVC, or MAX_PATH + * Name is limited to CHANNEL_NAME_LEN for an SVC, or MAX_PATH * bytes for a DVC */ - char chan_name[MAX(CHANNEL_NAME_BYTES, MAX_PATH) + 1]; + char chan_name[MAX(CHANNEL_NAME_LEN, MAX_PATH) + 1]; unsigned int channel_name_bytes; //g_writeln("my_api_trans_data_in: extra_flags %d", trans->extra_flags); From bfffc40cbaf7d783ed166f97507bec668a2df40f Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 26 May 2020 12:02:11 +0100 Subject: [PATCH 09/29] Ignore TS_MULTIFRAGMENTUPDATE_CAPABILITYSET from client if fp disabled --- libxrdp/xrdp_caps.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libxrdp/xrdp_caps.c b/libxrdp/xrdp_caps.c index ead495ca..854d23b1 100644 --- a/libxrdp/xrdp_caps.c +++ b/libxrdp/xrdp_caps.c @@ -561,7 +561,10 @@ xrdp_caps_process_multifragmentupdate(struct xrdp_rdp *self, struct stream *s, int MaxRequestSize; in_uint32_le(s, MaxRequestSize); - self->client_info.max_fastpath_frag_bytes = MaxRequestSize; + if (self->client_info.use_fast_path & 1) + { + self->client_info.max_fastpath_frag_bytes = MaxRequestSize; + } return 0; } From 81e037e964629cba96e4d3c96f7ced969f032a51 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 29 May 2020 13:28:04 +0100 Subject: [PATCH 10/29] Prevent chansrv input channels being scanned during a server reset --- libxrdp/libxrdp.c | 11 ++++++++++- libxrdp/libxrdpinc.h | 1 + libxrdp/xrdp_fastpath.c | 5 ++++- libxrdp/xrdp_mcs.c | 7 +++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/libxrdp/libxrdp.c b/libxrdp/libxrdp.c index 0160bcc8..96d32752 100644 --- a/libxrdp/libxrdp.c +++ b/libxrdp/libxrdp.c @@ -47,6 +47,7 @@ libxrdp_init(tbus id, struct trans *trans) session->rdp = xrdp_rdp_create(session, trans); session->orders = xrdp_orders_create(session, (struct xrdp_rdp *)session->rdp); session->client_info = &(((struct xrdp_rdp *)session->rdp)->client_info); + session->check_for_app_input = 1; return session; } @@ -1078,7 +1079,12 @@ libxrdp_reset(struct xrdp_session *session, return 1; } - /* shut down the rdp client */ + /* shut down the rdp client + * + * When resetting the lib, disable application input checks, as + * otherwise we can send a channel message to the other end while + * the channels are inactive ([MS-RDPBCGR] 3.2.5.5.1 */ + session->check_for_app_input = 0; if (xrdp_rdp_send_deactivate((struct xrdp_rdp *)session->rdp) != 0) { return 1; @@ -1090,6 +1096,9 @@ libxrdp_reset(struct xrdp_session *session, return 1; } + /* Re-enable application input checks */ + session->check_for_app_input = 1; + return 0; } diff --git a/libxrdp/libxrdpinc.h b/libxrdp/libxrdpinc.h index c236bec9..e235e6e6 100644 --- a/libxrdp/libxrdpinc.h +++ b/libxrdp/libxrdpinc.h @@ -66,6 +66,7 @@ struct xrdp_session struct trans *trans; int (*callback)(intptr_t id, int msg, intptr_t param1, intptr_t param2, intptr_t param3, intptr_t param4); + int check_for_app_input; void *rdp; void *orders; struct xrdp_client_info *client_info; diff --git a/libxrdp/xrdp_fastpath.c b/libxrdp/xrdp_fastpath.c index 711f2b85..fc0ac8a5 100644 --- a/libxrdp/xrdp_fastpath.c +++ b/libxrdp/xrdp_fastpath.c @@ -146,7 +146,10 @@ xrdp_fastpath_send(struct xrdp_fastpath *self, struct stream *s) { return 1; } - xrdp_fastpath_session_callback(self, 0x5556, 0, 0, 0, 0); + if (self->session->check_for_app_input) + { + xrdp_fastpath_session_callback(self, 0x5556, 0, 0, 0, 0); + } return 0; } diff --git a/libxrdp/xrdp_mcs.c b/libxrdp/xrdp_mcs.c index 832cc408..a0502ef1 100644 --- a/libxrdp/xrdp_mcs.c +++ b/libxrdp/xrdp_mcs.c @@ -997,8 +997,11 @@ xrdp_mcs_call_callback(struct xrdp_mcs *self) { if (session->callback != 0) { - /* in xrdp_wm.c */ - rv = session->callback(session->id, 0x5556, 0, 0, 0, 0); + if (session->check_for_app_input) + { + /* in xrdp_wm.c */ + rv = session->callback(session->id, 0x5556, 0, 0, 0, 0); + } } else { From 3c4b42b1aa25d7af78812917c8a57628cfa00633 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 30 Apr 2019 12:24:19 +0100 Subject: [PATCH 11/29] Implemented resize and multimon support for VNC backend --- docs/man/sesman.ini.5.in | 11 +- docs/man/xrdp.ini.5.in | 8 + libxrdp/libxrdp.c | 23 +- sesman/session.c | 3 - sesman/tools/sesrun.c | 8 +- vnc/vnc.c | 1145 ++++++++++++++++++++++++++++++++++---- vnc/vnc.h | 211 ++++--- xrdp/xrdp.ini.in | 4 + xrdp/xrdp_mm.c | 10 +- 9 files changed, 1213 insertions(+), 210 deletions(-) diff --git a/docs/man/sesman.ini.5.in b/docs/man/sesman.ini.5.in index 8fca3a9e..58bb74b5 100644 --- a/docs/man/sesman.ini.5.in +++ b/docs/man/sesman.ini.5.in @@ -148,12 +148,12 @@ This works only with xorgxrdp session. Moreover, xorgxrdp must be v0.2.9 or late .TP \fBPolicy\fR=\fI[Default|UBD|UBI|UBC|UBDI|UBDC]\fR -Session allocation policy. By default, a new session is created -for the combination when using Xrdp, and -for the combination when using Xvnc. -This behavior can be changed by setting session policy to: +Session allocation policy. Used to decide when to allocate a +new session. Set to one of the following values: .br +.br +\fBDefault\fR - session per .br \fBUBD\fR - session per .br @@ -168,7 +168,8 @@ This behavior can be changed by setting session policy to: .br Note that the \fBUser\fR and \fBBitPerPixel\fR criteria cannot be turned -off. For Xvnc connections, \fBDisplaySize\fR is always enabled as well. +off. \fBDisplaySize\fR refers to the initial geometry of a connection, +as actual display sizes can change dynamically. .br .SH "SECURITY" diff --git a/docs/man/xrdp.ini.5.in b/docs/man/xrdp.ini.5.in index 834f258b..d7bce514 100644 --- a/docs/man/xrdp.ini.5.in +++ b/docs/man/xrdp.ini.5.in @@ -302,6 +302,14 @@ Specifies color depth of the backend X server. The default is the color depth of the client. Only Xvnc and X11rdp use that setting. Xorg runs at \fI24\fR bpp. +.TP +\fBdisabled_encodings_mask\fR=\fI\fR +Set this bitmask to a non-zero value to prevent \fBxrdp\fR(8) requesting +some features from the Xvnc server. You should only need to set this +to a non-zero value to work around bugs in your Xvnc server. The bit +values supported for a particular release of \fBxrdp\fR(8) are documented in +\fBxrdp.ini\fR. + .TP \fBcode\fR=\fI\fR|\fI0\fR Specifies the session type. The default, \fI0\fR, is Xvnc, \fI10\fR is diff --git a/libxrdp/libxrdp.c b/libxrdp/libxrdp.c index 96d32752..cdb463b7 100644 --- a/libxrdp/libxrdp.c +++ b/libxrdp/libxrdp.c @@ -1044,29 +1044,36 @@ libxrdp_orders_send_font(struct xrdp_session *session, } /*****************************************************************************/ +/* Note : if this is called on a multimon setup, the client is resized + * to a single monitor */ int EXPORT_CC libxrdp_reset(struct xrdp_session *session, int width, int height, int bpp) { if (session->client_info != 0) { + struct xrdp_client_info *client_info = session->client_info; + /* older client can't resize */ - if (session->client_info->build <= 419) + if (client_info->build <= 419) { return 0; } - /* if same, don't need to do anything */ - if (session->client_info->width == width && - session->client_info->height == height && - session->client_info->bpp == bpp) + /* if same (and only one monitor on client) don't need to do anything */ + if (client_info->width == width && + client_info->height == height && + client_info->bpp == bpp && + (client_info->monitorCount == 0 || client_info->multimon == 0)) { return 0; } - session->client_info->width = width; - session->client_info->height = height; - session->client_info->bpp = bpp; + client_info->width = width; + client_info->height = height; + client_info->bpp = bpp; + client_info->monitorCount = 0; + client_info->multimon = 0; } else { diff --git a/sesman/session.c b/sesman/session.c index 9fab0392..f85616c1 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -105,9 +105,6 @@ session_get_bydata(const char *name, int width, int height, int bpp, int type, { case SCP_SESSION_TYPE_XVNC: /* 0 */ type = SESMAN_SESSION_TYPE_XVNC; /* 2 */ - /* Xvnc cannot resize */ - policy = (enum SESMAN_CFG_SESS_POLICY) - (policy | SESMAN_CFG_SESS_POLICY_D); break; case SCP_SESSION_TYPE_XRDP: /* 1 */ type = SESMAN_SESSION_TYPE_XRDP; /* 1 */ diff --git a/sesman/tools/sesrun.c b/sesman/tools/sesrun.c index 57335dd1..e7a00004 100644 --- a/sesman/tools/sesrun.c +++ b/sesman/tools/sesrun.c @@ -31,6 +31,10 @@ #include "sesman.h" #include "tcp.h" +#if !defined(PACKAGE_VERSION) +#define PACKAGE_VERSION "???" +#endif + struct config_sesman g_cfg; /* config.h */ /******************************************************************************/ @@ -61,9 +65,9 @@ main(int argc, char **argv) if (argc == 1) { - g_printf("xrdp session starter v0.1\n"); + g_printf("xrdp session starter v" PACKAGE_VERSION "\n"); g_printf("\nusage:\n"); - g_printf("sesrun \n"); + g_printf("sesrun \n"); g_printf("session code 0 for Xvnc, 10 for X11RDP, 20 for Xorg\n"); } else if (argc == 8) diff --git a/vnc/vnc.c b/vnc/vnc.c index 86eff852..555c8b7d 100644 --- a/vnc/vnc.c +++ b/vnc/vnc.c @@ -16,6 +16,14 @@ * limitations under the License. * * libvnc + * + * The message definitions used in this source file can be found mostly + * in RFC6143 - "The Remote Framebuffer Protocol". + * + * The ExtendedDesktopSize encoding is reserved in RFC6143, but not + * documented there. It is documented by the RFB protocol community + * wiki currently held at https://github.com/rfbproto/rfbroto. This is + * referred to below as the "RFB community wiki" */ #if defined(HAVE_CONFIG_H) @@ -26,21 +34,77 @@ #include "log.h" #include "trans.h" #include "ssl_calls.h" +#include "xrdp_client_info.h" #define LLOG_LEVEL 1 #define LLOGLN(_level, _args) \ - do \ - { \ - if (_level < LLOG_LEVEL) \ + do \ { \ - g_write("xrdp:vnc [%10.10u]: ", g_time3()); \ - g_writeln _args ; \ + if (_level < LLOG_LEVEL) \ + { \ + g_write("xrdp:vnc [%10.10u]: ", g_time3()); \ + g_writeln _args ; \ + } \ } \ - } \ - while (0) + while (0) #define AS_LOG_MESSAGE log_message +/* Client-to-server messages */ +enum c2s +{ + C2S_SET_PIXEL_FORMAT = 0, + C2S_SET_ENCODINGS = 2, + C2S_FRAMEBUFFER_UPDATE_REQUEST = 3, + C2S_KEY_EVENT = 4, + C2S_POINTER_EVENT = 5, + C2S_CLIENT_CUT_TEXT = 6, +}; + +/* Server to client messages */ +enum s2c +{ + S2C_FRAMEBUFFER_UPDATE = 0, + S2C_SET_COLOUR_MAP_ENTRIES = 1, + S2C_BELL = 2, + S2C_SERVER_CUT_TEXT = 3 +}; + +/* Encodings and pseudo-encodings + * + * The RFC uses a signed type for these. We use an unsigned type as the + * binary representation for the negative values is standardised in C + * (which it wouldn't be for an enum value) + */ + +typedef uint32_t encoding_type; + +#define ENC_RAW (encoding_type)0 +#define ENC_COPY_RECT (encoding_type)1 +#define ENC_CURSOR (encoding_type)-239 +#define ENC_DESKTOP_SIZE (encoding_type)-223 +#define ENC_EXTENDED_DESKTOP_SIZE (encoding_type)-308 + +/* Messages for ExtendedDesktopSize status code */ +static const char *eds_status_msg[] = +{ + /* 0 */ "No error", + /* 1 */ "Resize is administratively prohibited", + /* 2 */ "Out of resources", + /* 3 */ "Invalid screen layout", + /* others */ "Unknown code" +}; + +/* elements in above list */ +#define EDS_STATUS_MSG_COUNT \ + (sizeof(eds_status_msg) / sizeof(eds_status_msg[0])) + +/* Used by enabled_encodings_mask */ +enum +{ + MSK_EXTENDED_DESKTOP_SIZE = (1 << 0) +}; + static int lib_mod_process_message(struct vnc *v, struct stream *s); @@ -223,7 +287,7 @@ lib_process_channel_data(struct vnc *v, int chanid, int flags, int size, make_stream(out_s); // Send the RFB message type (CLIENT_CUT_TEXT) to the vnc server. init_stream(out_s, clip_bytes + 1 + 3 + 4 + 16); - out_uint8(out_s, 6); // RFB msg type: CLIENT_CUT_TEXT + out_uint8(out_s, C2S_CLIENT_CUT_TEXT); out_uint8s(out_s, 3); // padding // Send the length of the cut-text to the vnc server. out_uint32_be(out_s, clip_bytes); @@ -255,7 +319,365 @@ lib_process_channel_data(struct vnc *v, int chanid, int flags, int size, return 0; } -/******************************************************************************/ +/**************************************************************************//** + * Logs a debug message containing a screen layout + * + * @param lvl Level to log at + * @param source Where the layout came from + * @param layout Layout to log + */ +static void +log_screen_layout(const enum logLevels lvl, const char *source, + const struct vnc_screen_layout *layout) +{ + unsigned int i; + char text[256]; + size_t pos; + int res; + + pos = 0; + res = g_snprintf(text, sizeof(text) - pos, + "Layout from %s (geom=%dx%d #screens=%u) :", + source, layout->total_width, layout->total_height, + layout->count); + + i = 0; + while (res > 0 && (size_t)res < sizeof(text) - pos && i < layout->count) + { + pos += res; + res = g_snprintf(&text[pos], sizeof(text) - pos, + " %d:(%dx%d+%d+%d)", + layout->s[i].id, + layout->s[i].width, layout->s[i].height, + layout->s[i].x, layout->s[i].y); + ++i; + } + log_message(lvl, "%s", text); +} + +/**************************************************************************//** + * Compares two vnc_screen structures + * + * @param a First structure + * @param b Second structure + * + * @return Suitable for sorting structures with ID as the primary key + */ +static int cmp_vnc_screen(const struct vnc_screen *a, + const struct vnc_screen *b) +{ + int result = 0; + if (a->id != b->id) + { + result = a->id - b->id; + } + else if (a->x != b->x) + { + result = a->x - b->x; + } + else if (a->y != b->y) + { + result = a->y - b->y; + } + else if (a->width != b->width) + { + result = a->width - b->width; + } + else if (a->height != b->height) + { + result = a->height - b->height; + } + + return result; +} + +/**************************************************************************//** + * Compares two vnc_screen_layout structures for equality + * @param a First layout + * @param b First layout + * @return != 0 if structures are equal + */ +static int vnc_screen_layouts_equal(const struct vnc_screen_layout *a, + const struct vnc_screen_layout *b) +{ + unsigned int i; + int result = (a->total_width == b->total_width && + a->total_height == b->total_height && + a->count == b->count); + if (result) + { + for (i = 0 ; result && i < a->count ; ++i) + { + result = (cmp_vnc_screen(&a->s[i], &b->s[i]) == 0); + } + } + + return result; +} + +/**************************************************************************//** + * Reads an extended desktop size rectangle from the VNC server + * + * @param v VNC object + * @param [out] layout Desired layout for server + * @return != 0 for error + * + * @pre The next octet read from v->trans is the number of screens + * @pre layout is not already allocated + * + * @post if call is successful, layout->s must be freed after use. + * @post Returned structure is in increasing ID order + * @post layout->total_width is untouched + * @post layout->total_height is untouched + */ +static int +read_extended_desktop_size_rect(struct vnc *v, + struct vnc_screen_layout *layout) +{ + struct stream *s; + int error; + unsigned int count; + struct vnc_screen *screens; + + layout->count = 0; + layout->s = NULL; + + make_stream(s); + init_stream(s, 8192); + + /* Read in the current screen config */ + error = trans_force_read_s(v->trans, s, 4); + if (error == 0) + { + /* Get the number of screens */ + in_uint8(s, count); + in_uint8s(s, 3); + + error = trans_force_read_s(v->trans, s, 16 * count); + if (error == 0) + { + screens = g_new(struct vnc_screen, count); + if (screens == NULL) + { + log_message(LOG_LEVEL_ERROR, + "VNC : Can't alloc for %d screens", count); + error = 1; + } + else + { + unsigned int i; + for (i = 0 ; i < count ; ++i) + { + in_uint32_be(s, screens[i].id); + in_uint16_be(s, screens[i].x); + in_uint16_be(s, screens[i].y); + in_uint16_be(s, screens[i].width); + in_uint16_be(s, screens[i].height); + in_uint32_be(s, screens[i].flags); + } + + /* sort monitors in increasing ID order */ + qsort(screens, count, sizeof(screens[0]), + (int (*)(const void *, const void *))cmp_vnc_screen); + } + } + } + + free_stream(s); + + if (error == 0) + { + layout->count = count; + layout->s = screens; + } + + return error; +} + +/**************************************************************************//** + * Sends a SetDesktopSize message + * + * @param v VNC object + * @param layout Desired layout for server + * @return != 0 for error + * + * The SetDesktopSize message is documented in the RFB community wiki + * "SetDesktopSize" section. + */ +static int +send_set_desktop_size(struct vnc *v, const struct vnc_screen_layout *layout) +{ + unsigned int i; + struct stream *s; + int error; + + make_stream(s); + init_stream(s, 8192); + out_uint8(s, 251); + out_uint8(s, 0); + out_uint16_be(s, layout->total_width); + out_uint16_be(s, layout->total_height); + + out_uint8(s, layout->count); + out_uint8(s, 0); + for (i = 0 ; i < layout->count ; ++i) + { + out_uint32_be(s, layout->s[i].id); + out_uint16_be(s, layout->s[i].x); + out_uint16_be(s, layout->s[i].y); + out_uint16_be(s, layout->s[i].width); + out_uint16_be(s, layout->s[i].height); + out_uint32_be(s, layout->s[i].flags); + } + s_mark_end(s); + log_message(LOG_LEVEL_DEBUG, "VNC Sending SetDesktopSize"); + error = lib_send_copy(v, s); + free_stream(s); + + return error; +} + +/**************************************************************************//** + * Sets up a single-screen vnc_screen_layout structure + * + * @param layout Structure to set up + * @param width New client width + * @param height New client height + * + * @pre layout->count must be valid + * @pre layout->s must be valid + */ +static void +set_single_screen_layout(struct vnc_screen_layout *layout, + int width, int height) +{ + int id = 0; + int flags = 0; + + layout->total_width = width; + layout->total_height = height; + + if (layout->count == 0) + { + /* No previous layout */ + layout->s = g_new(struct vnc_screen, 1); + } + else + { + /* Keep the ID and flags from the previous first screen */ + id = layout->s[0].id; + flags = layout->s[0].flags; + + if (layout->count > 1) + { + g_free(layout->s); + layout->s = g_new(struct vnc_screen, 1); + } + } + layout->count = 1; + layout->s[0].id = id; + layout->s[0].x = 0; + layout->s[0].y = 0; + layout->s[0].width = width; + layout->s[0].height = height; + layout->s[0].flags = flags; +} + +/**************************************************************************//** + * Resize the client as a single screen + * + * @param v VNC object + * @param update_in_progress True if there's a painter update in progress + * @param width New client width + * @param height New client height + * @return != 0 for error + * + * The new client layout is recorded in v->client_layout. If the client was + * multi-screen before this call, it won't be afterwards. + */ +static int +resize_client(struct vnc *v, int update_in_progress, int width, int height) +{ + int error = 0; + + if (v->client_layout.count != 1 || + v->client_layout.total_width != width || + v->client_layout.total_height != height) + { + if (update_in_progress) + { + error = v->server_end_update(v); + } + + if (error == 0) + { + error = v->server_reset(v, width, height, v->server_bpp); + if (error == 0) + { + set_single_screen_layout(&v->client_layout, width, height); + if (update_in_progress) + { + error = v->server_begin_update(v); + } + } + } + } + + return error; +} + + +/**************************************************************************//** + * Resize the attached client from a layout + * + * @param v VNC object + * @param update_in_progress True if there's a painter update in progress + * @param layout Desired layout from server + * @return != 0 for error + * + * This has some limitations. We have no way to move multiple screens about + * on a connected client, and so we are not able to change the client unless + * we're changing to a single screeen layout. + */ +static int +resize_client_from_layout(struct vnc *v, + int update_in_progress, + const struct vnc_screen_layout *layout) +{ + int error = 0; + + if (!vnc_screen_layouts_equal(&v->client_layout, layout)) + { + /* + * we don't have the capability to resize to anything other + * than a single screen. + */ + if (layout->count != 1) + { + log_message(LOG_LEVEL_ERROR, + "VNC Resize to %d screen(s) from %d screen(s) " + "not implemented", + v->client_layout.count, layout->count); + + /* Dump some useful info, in case we get here when we don't + * need to */ + log_screen_layout(LOG_LEVEL_ERROR, "OldLayout", &v->client_layout); + log_screen_layout(LOG_LEVEL_ERROR, "NewLayout", layout); + error = 1; + } + else + { + error = resize_client(v, + update_in_progress, + layout->total_width, + layout->total_height); + } + } + + return error; +} + +/*****************************************************************************/ int lib_mod_event(struct vnc *v, int msg, long param1, long param2, long param3, long param4) @@ -309,7 +731,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2, { /* fix for mstsc sending left control down with altgr */ init_stream(s, 8192); - out_uint8(s, 4); + out_uint8(s, C2S_KEY_EVENT); out_uint8(s, 0); /* down flag */ out_uint8s(s, 2); out_uint32_be(s, 65507); /* left control */ @@ -319,7 +741,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2, } init_stream(s, 8192); - out_uint8(s, 4); + out_uint8(s, C2S_KEY_EVENT); out_uint8(s, msg == 15); /* down flag */ out_uint8s(s, 2); out_uint32_be(s, key); @@ -371,7 +793,7 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2, } init_stream(s, 8192); - out_uint8(s, 5); + out_uint8(s, C2S_POINTER_EVENT); out_uint8(s, v->mod_mouse_state); out_uint16_be(s, param1); out_uint16_be(s, param2); @@ -382,10 +804,9 @@ lib_mod_event(struct vnc *v, int msg, long param1, long param2, { if (v->suppress_output == 0) { - /* FramebufferUpdateRequest */ init_stream(s, 8192); - out_uint8(s, 3); - out_uint8(s, 0); + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 0); /* incremental == 0 : Full contents */ x = (param1 >> 16) & 0xffff; out_uint16_be(s, x); y = param1 & 0xffff; @@ -586,6 +1007,463 @@ make_color(int r, int g, int b, int bpp) return 0; } +/** + * Converts a bits-per-pixel value to bytes-per-pixel + */ +static int +get_bytes_per_pixel(int bpp) +{ + int result = (bpp + 7) / 8; + + if (result == 3) + { + result = 4; + } + + return result; +} + + +/**************************************************************************//** + * Skips the specified number of bytes from the transport + * + * @param transport Transport to read + * @param bytes Bytes to skip + * @return != 0 for error + */ +static int +skip_trans_bytes(struct trans *trans, int bytes) +{ + struct stream *s; + int error; + + make_stream(s); + init_stream(s, bytes); + error = trans_force_read_s(trans, s, bytes); + free_stream(s); + + return error; +} + +/**************************************************************************//** + * Reads an encoding from the input stream and discards it + * + * @param v VNC object + * @param x Encoding X value + * @param y Encoding Y value + * @param cx Encoding CX value + * @param cy Encoding CY value + * @param encoding Code for encoding + * @return != 0 for error + * + * @pre On entry the input stream is positioned after the encoding header + */ +static int +skip_encoding(struct vnc *v, int x, int y, int cx, int cy, + encoding_type encoding) +{ + char text[256]; + int error = 0; + + switch (encoding) + { + case ENC_RAW: + { + int need_size = cx * cy * get_bytes_per_pixel(v->server_bpp); + log_message(LOG_LEVEL_DEBUG, "Skipping ENC_RAW encoding"); + error = skip_trans_bytes(v->trans, need_size); + } + break; + + case ENC_COPY_RECT: + { + log_message(LOG_LEVEL_DEBUG, "Skipping ENC_COPY_RECT encoding"); + error = skip_trans_bytes(v->trans, 4); + } + break; + + case ENC_CURSOR: + { + int j = cx * cy * get_bytes_per_pixel(v->server_bpp); + int k = ((cx + 7) / 8) * cy; + + log_message(LOG_LEVEL_DEBUG, "Skipping ENC_CURSOR encoding"); + error = skip_trans_bytes(v->trans, j + k); + } + break; + + case ENC_DESKTOP_SIZE: + log_message(LOG_LEVEL_DEBUG, "Skipping ENC_DESKTOP_SIZE encoding"); + break; + + case ENC_EXTENDED_DESKTOP_SIZE: + { + struct vnc_screen_layout layout = {0}; + log_message(LOG_LEVEL_DEBUG, + "Skipping ENC_EXTENDED_DESKTOP_SIZE encoding " + "x=%d, y=%d geom=%dx%d", + x, y, cx, cy); + error = read_extended_desktop_size_rect(v, &layout); + g_free(layout.s); + } + break; + + default: + g_sprintf(text, "VNC error in skip_encoding " + "encoding = %8.8x", encoding); + v->server_msg(v, text, 1); + } + + return error; +} + +/**************************************************************************//** + * Parses an entire framebuffer update message from the wire, and returns the + * first matching ExtendedDesktopSize encoding if found. + * + * Caller can check for a match by examining match_layout.s after the call + * + * @param v VNC object + * @param match Function to call to check for a match + * @param [out] match_x Matching x parameter for an encoding (if needed) + * @param [out] match_y Matching y parameter for an encoding (if needed) + * @param [out] match_layout Returned layout for the encoding + * @return != 0 for error + * + * @post After a successful call, match_layout.s must be free'd + */ +static int +find_matching_extended_rect(struct vnc *v, + int (*match)(int x, int y, int cx, int cy), + int *match_x, + int *match_y, + struct vnc_screen_layout *match_layout) +{ + int error; + struct stream *s; + unsigned int num_rects; + unsigned int i; + int x; + int y; + int cx; + int cy; + encoding_type encoding; + + match_layout->s = NULL; + + make_stream(s); + init_stream(s, 8192); + error = trans_force_read_s(v->trans, s, 3); + + if (error == 0) + { + in_uint8s(s, 1); + in_uint16_be(s, num_rects); + + for (i = 0; i < num_rects; ++i) + { + if (error != 0) + { + break; + } + + init_stream(s, 8192); + error = trans_force_read_s(v->trans, s, 12); + + if (error == 0) + { + in_uint16_be(s, x); + in_uint16_be(s, y); + in_uint16_be(s, cx); + in_uint16_be(s, cy); + in_uint32_be(s, encoding); + + if (encoding == ENC_EXTENDED_DESKTOP_SIZE && + match_layout->s == NULL && + match(x, y, cx, cy)) + { + log_message(LOG_LEVEL_DEBUG, + "VNC matched ExtendedDesktopSize rectangle " + "x=%d, y=%d geom=%dx%d", + x, y, cx, cy); + + error = read_extended_desktop_size_rect(v, match_layout); + if (match_x) + { + *match_x = x; + } + if (match_y) + { + *match_y = y; + } + match_layout->total_width = cx; + match_layout->total_height = cy; + } + else + { + error = skip_encoding(v, x, y, cx, cy, encoding); + } + } + } + } + + return error; +} + +/**************************************************************************//** + * Sends a FramebufferUpdateRequest for the resize status state machine + * + * The state machine is used at the start of the connection to negotiate + * a common geometry between the client and the server. + * + * The RFB community wiki contains the following paragraph not present + * in RFC6143:- + * + * Note that an empty area can still solicit a FramebufferUpdate + * even though that update will only contain pseudo-encodings + * + * This doesn't seem to be as widely supported as we would like at + * present. We will always request at least a single pixel update to + * avoid confusing the server. + * + * @param v VNC object + * @return != 0 for error + */ +static int +send_update_request_for_resize_status(struct vnc *v) +{ + int error = 0; + struct stream *s; + make_stream(s); + init_stream(s, 8192); + + switch (v->initial_resize_status) + { + case VRS_WAITING_FOR_FIRST_UPDATE: + /* + * Ask for an immediate, minimal update. + */ + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 0); /* incremental == 0 : Full update */ + out_uint16_be(s, 0); + out_uint16_be(s, 0); + out_uint16_be(s, 1); + out_uint16_be(s, 1); + s_mark_end(s); + error = lib_send_copy(v, s); + break; + + case VRS_WAITING_FOR_RESIZE_CONFIRM: + /* + * Ask for a deferred minimal update. + */ + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 1); /* incremental == 1 : Changes only */ + out_uint16_be(s, 0); + out_uint16_be(s, 0); + out_uint16_be(s, 1); + out_uint16_be(s, 1); + s_mark_end(s); + error = lib_send_copy(v, s); + break; + + default: + /* + * Ask for a full update from the server + */ + if (v->suppress_output == 0) + { + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 0); /* incremental == 0 : Full update */ + out_uint16_be(s, 0); + out_uint16_be(s, 0); + out_uint16_be(s, v->server_width); + out_uint16_be(s, v->server_height); + s_mark_end(s); + error = lib_send_copy(v, s); + } + break; + } + + free_stream(s); + + return error; +} + +/**************************************************************************//** + * Tests if extended desktop size rect is an initial geometry specification + * + * This should be x == 0, but the specification says to treat undefined + * values as 0 also */ +static int +rect_is_initial_geometry(int x, int y, int cx, int cy) +{ + return (x != 1 && x != 2); +} + +/**************************************************************************//** + * Tests if extended desktop size rect is a reply to a request from us + */ +static int +rect_is_reply_to_us(int x, int y, int cx, int cy) +{ + return (x == 1); +} + +/**************************************************************************//** + * Returns an error string for an ExtendedDesktopSize status code + */ +static const char * +get_eds_status_msg(unsigned int response_code) +{ + if (response_code >= EDS_STATUS_MSG_COUNT) + { + response_code = EDS_STATUS_MSG_COUNT - 1; + } + + return eds_status_msg[response_code]; +} + +/**************************************************************************//** + * Handles the first framebuffer update from the server + * + * This is used to determine if the server supports resizes from + * us. See The RFB community wiki for details. + * + * If the server does support resizing, we send our client geometry over. + * + * @param v VNC object + * @return != 0 for error + */ +static int +lib_framebuffer_first_update(struct vnc *v) +{ + int error; + struct vnc_screen_layout layout = {0}; + + error = find_matching_extended_rect(v, + rect_is_initial_geometry, + NULL, + NULL, + &layout); + if (error == 0) + { + if (layout.s != NULL) + { + log_message(LOG_LEVEL_DEBUG, "VNC server supports resizing"); + + /* Force the client geometry over to the server */ + log_screen_layout(LOG_LEVEL_INFO, "OldLayout", &layout); + + /* + * If we've only got one screen, and the other side has + * only got one screen, we will preserve their screen ID + * and any flags. This may prevent us sending an unwanted + * SetDesktopSize message if the screen dimensions are + * a match. We can't do this with more than one screen, + * as we have no way to map different IDs + */ + if (layout.count == 1 && v->client_layout.count == 1) + { + log_message(LOG_LEVEL_DEBUG, "VNC " + "setting screen id to %d from server", + layout.s[0].id); + + v->client_layout.s[0].id = layout.s[0].id; + v->client_layout.s[0].flags = layout.s[0].flags; + } + + if (vnc_screen_layouts_equal(&layout, &v->client_layout)) + { + log_message(LOG_LEVEL_DEBUG, "Server layout is the same " + "as the client layout"); + v->initial_resize_status = VRS_DONE; + } + else + { + log_message(LOG_LEVEL_DEBUG, "Server layout differs from " + "the client layout. Changing server layout"); + error = send_set_desktop_size(v, &v->client_layout); + v->initial_resize_status = VRS_WAITING_FOR_RESIZE_CONFIRM; + } + } + else + { + log_message(LOG_LEVEL_DEBUG, "VNC server does not support resizing"); + + /* Force client to same size as server */ + log_message(LOG_LEVEL_DEBUG, "Resizing client to server %dx%d", + v->server_width, v->server_height); + error = resize_client(v, 0, v->server_width, v->server_height); + v->initial_resize_status = VRS_DONE; + } + + g_free(layout.s); + } + + if (error == 0) + { + error = send_update_request_for_resize_status(v); + } + + return error; +} + +/**************************************************************************//** + * Looks for a resize confirm in a framebuffer update request + * + * If the server supports resizes from us, this is used to find the + * reply to our initial resize request. See The RFB community wiki for details. + * + * @param v VNC object + * @return != 0 for error + */ +static int +lib_framebuffer_waiting_for_resize_confirm(struct vnc *v) +{ + int error; + struct vnc_screen_layout layout = {0}; + int response_code; + + error = find_matching_extended_rect(v, + rect_is_reply_to_us, + NULL, + &response_code, + &layout); + if (error == 0) + { + if (layout.s != NULL) + { + if (response_code == 0) + { + log_message(LOG_LEVEL_DEBUG, "VNC server successfully resized"); + log_screen_layout(LOG_LEVEL_INFO, "NewLayout", &layout); + } + else + { + log_message(LOG_LEVEL_ERROR, + "VNC server resize failed - error code %d [%s]", + response_code, + get_eds_status_msg(response_code)); + /* Force client to same size as server */ + log_message(LOG_LEVEL_DEBUG, "Resizing client to server %dx%d", + v->server_width, v->server_height); + error = resize_client(v, 0, v->server_width, v->server_height); + } + v->initial_resize_status = VRS_DONE; + } + + g_free(layout.s); + } + + if (error == 0) + { + error = send_update_request_for_resize_status(v); + } + + return error; +} + /******************************************************************************/ int lib_framebuffer_update(struct vnc *v) @@ -606,7 +1484,6 @@ lib_framebuffer_update(struct vnc *v) int srcx; int srcy; unsigned int encoding; - int Bpp; int pixel; int r; int g; @@ -615,14 +1492,9 @@ lib_framebuffer_update(struct vnc *v) int need_size; struct stream *s; struct stream *pixel_s; + struct vnc_screen_layout layout = { 0 }; num_recs = 0; - Bpp = (v->mod_bpp + 7) / 8; - - if (Bpp == 3) - { - Bpp = 4; - } make_stream(pixel_s); @@ -655,9 +1527,9 @@ lib_framebuffer_update(struct vnc *v) in_uint16_be(s, cy); in_uint32_be(s, encoding); - if (encoding == 0) /* raw */ + if (encoding == ENC_RAW) { - need_size = cx * cy * Bpp; + need_size = cx * cy * get_bytes_per_pixel(v->server_bpp); init_stream(pixel_s, need_size); error = trans_force_read_s(v->trans, pixel_s, need_size); @@ -666,7 +1538,7 @@ lib_framebuffer_update(struct vnc *v) error = v->server_paint_rect(v, x, y, cx, cy, pixel_s->data, cx, cy, 0, 0); } } - else if (encoding == 1) /* copy rect */ + else if (encoding == ENC_COPY_RECT) { init_stream(s, 8192); error = trans_force_read_s(v->trans, s, 4); @@ -678,11 +1550,11 @@ lib_framebuffer_update(struct vnc *v) error = v->server_screen_blt(v, x, y, cx, cy, srcx, srcy); } } - else if (encoding == 0xffffff11) /* cursor */ + else if (encoding == ENC_CURSOR) { g_memset(cursor_data, 0, 32 * (32 * 3)); g_memset(cursor_mask, 0, 32 * (32 / 8)); - j = cx * cy * Bpp; + j = cx * cy * get_bytes_per_pixel(v->server_bpp); k = ((cx + 7) / 8) * cy; init_stream(s, j + k); error = trans_force_read_s(v->trans, s, j + k); @@ -701,8 +1573,8 @@ lib_framebuffer_update(struct vnc *v) if (pixel) { - pixel = get_pixel_safe(d1, k, 31 - j, cx, cy, v->mod_bpp); - split_color(pixel, &r, &g, &b, v->mod_bpp, v->palette); + pixel = get_pixel_safe(d1, k, 31 - j, cx, cy, v->server_bpp); + split_color(pixel, &r, &g, &b, v->server_bpp, v->palette); pixel = make_color(r, g, b, 24); set_pixel_safe(cursor_data, k, j, 32, 32, 24, pixel); } @@ -723,11 +1595,26 @@ lib_framebuffer_update(struct vnc *v) error = v->server_set_cursor(v, x, y, cursor_data, cursor_mask); } } - else if (encoding == 0xffffff21) /* desktop size */ + else if (encoding == ENC_DESKTOP_SIZE) { - v->mod_width = cx; - v->mod_height = cy; - error = v->server_reset(v, cx, cy, v->mod_bpp); + /* Server end has resized */ + v->server_width = cx; + v->server_height = cy; + error = resize_client(v, 1, cx, cy); + } + else if (encoding == ENC_EXTENDED_DESKTOP_SIZE) + { + layout.total_width = cx; + layout.total_height = cy; + error = read_extended_desktop_size_rect(v, &layout); + /* If this is a reply to a request from us, x == 1 */ + if (error == 0 && x != 1) + { + v->server_width = layout.total_width; + v->server_height = layout.total_height; + error = resize_client_from_layout(v, 1, &layout); + } + g_free(layout.s); } else { @@ -747,14 +1634,13 @@ lib_framebuffer_update(struct vnc *v) { if (v->suppress_output == 0) { - /* FramebufferUpdateRequest */ init_stream(s, 8192); - out_uint8(s, 3); - out_uint8(s, 1); + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 1); /* incremental == 1 : Changes only */ out_uint16_be(s, 0); out_uint16_be(s, 0); - out_uint16_be(s, v->mod_width); - out_uint16_be(s, v->mod_height); + out_uint16_be(s, v->server_width); + out_uint16_be(s, v->server_height); s_mark_end(s); error = lib_send_copy(v, s); } @@ -903,19 +1789,31 @@ lib_mod_process_message(struct vnc *v, struct stream *s) error = 0; if (error == 0) { - if (type == 0) /* framebuffer update */ + if (type == S2C_FRAMEBUFFER_UPDATE) { - error = lib_framebuffer_update(v); + switch (v->initial_resize_status) + { + case VRS_WAITING_FOR_FIRST_UPDATE: + error = lib_framebuffer_first_update(v); + break; + + case VRS_WAITING_FOR_RESIZE_CONFIRM: + error = lib_framebuffer_waiting_for_resize_confirm(v); + break; + + default: + error = lib_framebuffer_update(v); + } } - else if (type == 1) /* palette */ + else if (type == S2C_SET_COLOUR_MAP_ENTRIES) { error = lib_palette_update(v); } - else if (type == 2) /* bell */ + else if (type == S2C_BELL) { error = lib_bell_trigger(v); } - else if (type == 3) /* clipboard */ + else if (type == S2C_SERVER_CUT_TEXT) /* clipboard */ { log_message(LOG_LEVEL_DEBUG, "VNC got clip data"); error = lib_clip_data(v); @@ -938,8 +1836,6 @@ lib_mod_start(struct vnc *v, int w, int h, int bpp) v->server_set_fgcolor(v, 0); v->server_fill_rect(v, 0, 0, w, h); v->server_end_update(v); - v->server_width = w; - v->server_height = h; v->server_bpp = bpp; return 0; } @@ -1026,7 +1922,7 @@ lib_mod_connect(struct vnc *v) default: v->server_msg(v, "VNC error - only supporting 8, 15, 16, 24 and 32 " "bpp rdp connections", 0); - return 1; + return 1; } if (g_strcmp(v->ip, "") == 0) @@ -1055,7 +1951,7 @@ lib_mod_connect(struct vnc *v) v->server_msg(v, text, 0); g_sleep(v->delay_ms); } - + g_sprintf(text, "VNC connecting to %s %s", v->ip, con_port); v->server_msg(v, text, 0); @@ -1188,8 +2084,9 @@ lib_mod_connect(struct vnc *v) if (error == 0) { - in_uint16_be(s, v->mod_width); - in_uint16_be(s, v->mod_height); + in_uint16_be(s, v->server_width); + in_uint16_be(s, v->server_height); + init_stream(pixel_format, 8192); v->server_msg(v, "VNC receiving pixel format", 0); error = trans_force_read_s(v->trans, pixel_format, 16); @@ -1201,7 +2098,6 @@ lib_mod_connect(struct vnc *v) if (error == 0) { - v->mod_bpp = v->server_bpp; init_stream(s, 8192); v->server_msg(v, "VNC receiving name length", 0); error = trans_force_read_s(v->trans, s, 4); /* name len */ @@ -1236,15 +2132,14 @@ lib_mod_connect(struct vnc *v) /* should be connected */ if (error == 0) { - /* SetPixelFormat */ init_stream(s, 8192); - out_uint8(s, 0); + out_uint8(s, C2S_SET_PIXEL_FORMAT); out_uint8(s, 0); out_uint8(s, 0); out_uint8(s, 0); init_stream(pixel_format, 8192); - if (v->mod_bpp == 8) + if (v->server_bpp == 8) { out_uint8(pixel_format, 8); /* bits per pixel */ out_uint8(pixel_format, 8); /* depth */ @@ -1262,7 +2157,7 @@ lib_mod_connect(struct vnc *v) out_uint8(pixel_format, 0); /* blue shift */ out_uint8s(pixel_format, 3); /* pad */ } - else if (v->mod_bpp == 15) + else if (v->server_bpp == 15) { out_uint8(pixel_format, 16); /* bits per pixel */ out_uint8(pixel_format, 15); /* depth */ @@ -1280,7 +2175,7 @@ lib_mod_connect(struct vnc *v) out_uint8(pixel_format, 0); /* blue shift */ out_uint8s(pixel_format, 3); /* pad */ } - else if (v->mod_bpp == 16) + else if (v->server_bpp == 16) { out_uint8(pixel_format, 16); /* bits per pixel */ out_uint8(pixel_format, 16); /* depth */ @@ -1298,7 +2193,7 @@ lib_mod_connect(struct vnc *v) out_uint8(pixel_format, 0); /* blue shift */ out_uint8s(pixel_format, 3); /* pad */ } - else if (v->mod_bpp == 24 || v->mod_bpp == 32) + else if (v->server_bpp == 24 || v->server_bpp == 32) { out_uint8(pixel_format, 32); /* bits per pixel */ out_uint8(pixel_format, 24); /* depth */ @@ -1325,50 +2220,41 @@ lib_mod_connect(struct vnc *v) if (error == 0) { - /* SetEncodings */ + encoding_type e[10]; + unsigned int n = 0; + unsigned int i; + + /* These encodings are always supported */ + e[n++] = ENC_RAW; + e[n++] = ENC_COPY_RECT; + e[n++] = ENC_CURSOR; + e[n++] = ENC_DESKTOP_SIZE; + if (v->enabled_encodings_mask & MSK_EXTENDED_DESKTOP_SIZE) + { + e[n++] = ENC_EXTENDED_DESKTOP_SIZE; + } + else + { + log_message(LOG_LEVEL_INFO, + "VNC User disabled EXTENDED_DESKTOP_SIZE"); + } + init_stream(s, 8192); - out_uint8(s, 2); + out_uint8(s, C2S_SET_ENCODINGS); out_uint8(s, 0); - out_uint16_be(s, 4); - out_uint32_be(s, 0); /* raw */ - out_uint32_be(s, 1); /* copy rect */ - out_uint32_be(s, 0xffffff11); /* cursor */ - out_uint32_be(s, 0xffffff21); /* desktop size */ - v->server_msg(v, "VNC sending encodings", 0); + out_uint16_be(s, n); /* Number of encodings following */ + for (i = 0 ; i < n; ++i) + { + out_uint32_be(s, e[i]); + } s_mark_end(s); error = trans_force_write_s(v->trans, s); } if (error == 0) { - error = v->server_reset(v, v->mod_width, v->mod_height, v->mod_bpp); - } - - if (error == 0) - { - if (v->suppress_output == 0) - { - /* FramebufferUpdateRequest */ - init_stream(s, 8192); - out_uint8(s, 3); - out_uint8(s, 0); - out_uint16_be(s, 0); - out_uint16_be(s, 0); - out_uint16_be(s, v->mod_width); - out_uint16_be(s, v->mod_height); - v->server_msg(v, "VNC sending framebuffer update request", 0); - s_mark_end(s); - error = trans_force_write_s(v->trans, s); - } - } - - if (error == 0) - { - if (v->server_bpp != v->mod_bpp) - { - v->server_msg(v, "VNC error - server bpp and client bpp do not match", 0); - error = 1; - } + v->initial_resize_status = VRS_WAITING_FOR_FIRST_UPDATE; + error = send_update_request_for_resize_status(v); } if (error == 0) @@ -1426,6 +2312,39 @@ lib_mod_end(struct vnc *v) return 0; } +/**************************************************************************//** + * Initialises the client layout from the Windows monitor definition. + * + * @param [out] layout Our layout + * @param [in] client_info WM info + */ +static void +init_client_layout(struct vnc_screen_layout *layout, + const struct xrdp_client_info *client_info) +{ + int i; + + layout->total_width = client_info->width; + layout->total_height = client_info->height; + + layout->count = client_info->monitorCount; + layout->s = g_new(struct vnc_screen, layout->count); + + for (i = 0 ; i < client_info->monitorCount ; ++i) + { + /* Use minfo_wm, as this is normalised for a top-left of (0,0) + * as required by RFC6143 */ + layout->s[i].id = i; + layout->s[i].x = client_info->minfo_wm[i].left; + layout->s[i].y = client_info->minfo_wm[i].top; + layout->s[i].width = client_info->minfo_wm[i].right - + client_info->minfo_wm[i].left + 1; + layout->s[i].height = client_info->minfo_wm[i].bottom - + client_info->minfo_wm[i].top + 1; + layout->s[i].flags = 0; + } +} + /******************************************************************************/ int lib_mod_set_param(struct vnc *v, const char *name, const char *value) @@ -1459,6 +2378,31 @@ lib_mod_set_param(struct vnc *v, const char *name, const char *value) v->got_guid = 1; g_memcpy(v->guid, value, 16); } + else if (g_strcasecmp(name, "disabled_encodings_mask") == 0) + { + v->enabled_encodings_mask = ~g_atoi(value); + } + else if (g_strcasecmp(name, "client_info") == 0) + { + const struct xrdp_client_info *client_info = + (const struct xrdp_client_info *) value; + + g_free(v->client_layout.s); + + /* Save monitor information from the client */ + if (!client_info->multimon || client_info->monitorCount < 1) + { + set_single_screen_layout(&v->client_layout, + client_info->width, + client_info->height); + } + else + { + init_client_layout(&v->client_layout, client_info); + } + log_screen_layout(LOG_LEVEL_DEBUG, "client_info", &v->client_layout); + } + return 0; } @@ -1504,7 +2448,7 @@ lib_mod_check_wait_objs(struct vnc *v) /******************************************************************************/ /* return error */ int -lib_mod_frame_ack(struct vnc* v, int flags, int frame_id) +lib_mod_frame_ack(struct vnc *v, int flags, int frame_id) { return 0; } @@ -1512,7 +2456,7 @@ lib_mod_frame_ack(struct vnc* v, int flags, int frame_id) /******************************************************************************/ /* return error */ int -lib_mod_suppress_output(struct vnc* v, int suppress, +lib_mod_suppress_output(struct vnc *v, int suppress, int left, int top, int right, int bottom) { int error; @@ -1522,15 +2466,14 @@ lib_mod_suppress_output(struct vnc* v, int suppress, v->suppress_output = suppress; if (suppress == 0) { - /* FramebufferUpdateRequest */ make_stream(s); init_stream(s, 8192); - out_uint8(s, 3); - out_uint8(s, 0); + out_uint8(s, C2S_FRAMEBUFFER_UPDATE_REQUEST); + out_uint8(s, 0); /* incremental == 0 : Full contents */ out_uint16_be(s, 0); out_uint16_be(s, 0); - out_uint16_be(s, v->mod_width); - out_uint16_be(s, v->mod_height); + out_uint16_be(s, v->server_width); + out_uint16_be(s, v->server_height); s_mark_end(s); error = lib_send_copy(v, s); free_stream(s); @@ -1559,6 +2502,9 @@ mod_init(void) v->mod_check_wait_objs = lib_mod_check_wait_objs; v->mod_frame_ack = lib_mod_frame_ack; v->mod_suppress_output = lib_mod_suppress_output; + + /* Member variables */ + v->enabled_encodings_mask = -1; return (tintptr) v; } @@ -1574,6 +2520,7 @@ mod_exit(tintptr handle) return 0; } trans_delete(v->trans); + g_free(v->client_layout.s); g_free(v); return 0; } diff --git a/vnc/vnc.h b/vnc/vnc.h index 5aa83472..1e1034c0 100644 --- a/vnc/vnc.h +++ b/vnc/vnc.h @@ -26,98 +26,129 @@ #define CURRENT_MOD_VER 4 +/* Screen used for ExtendedDesktopSize / Set DesktopSize */ +struct vnc_screen +{ + int id; + int x; + int y; + int width; + int height; + int flags; +}; + +struct vnc_screen_layout +{ + int total_width; + int total_height; + unsigned int count; + /* For comparison, screens are sorted in increasing order of ID */ + struct vnc_screen *s; +}; + +/** + * Keep track of resize status at start of connection + */ +enum vnc_resize_status +{ + VRS_WAITING_FOR_FIRST_UPDATE, + VRS_WAITING_FOR_RESIZE_CONFIRM, + VRS_DONE +}; + struct vnc { - int size; /* size of this struct */ - int version; /* internal version */ - /* client functions */ - int (*mod_start)(struct vnc* v, int w, int h, int bpp); - int (*mod_connect)(struct vnc* v); - int (*mod_event)(struct vnc* v, int msg, long param1, long param2, - long param3, long param4); - int (*mod_signal)(struct vnc* v); - int (*mod_end)(struct vnc* v); - int (*mod_set_param)(struct vnc *v, const char *name, const char *value); - int (*mod_session_change)(struct vnc* v, int, int); - int (*mod_get_wait_objs)(struct vnc* v, tbus* read_objs, int* rcount, - tbus* write_objs, int* wcount, int* timeout); - int (*mod_check_wait_objs)(struct vnc* v); - int (*mod_frame_ack)(struct vnc* v, int flags, int frame_id); - int (*mod_suppress_output)(struct vnc* v, int suppress, - int left, int top, int right, int bottom); - tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod + int size; /* size of this struct */ + int version; /* internal version */ + /* client functions */ + int (*mod_start)(struct vnc *v, int w, int h, int bpp); + int (*mod_connect)(struct vnc *v); + int (*mod_event)(struct vnc *v, int msg, long param1, long param2, + long param3, long param4); + int (*mod_signal)(struct vnc *v); + int (*mod_end)(struct vnc *v); + int (*mod_set_param)(struct vnc *v, const char *name, const char *value); + int (*mod_session_change)(struct vnc *v, int, int); + int (*mod_get_wait_objs)(struct vnc *v, tbus *read_objs, int *rcount, + tbus *write_objs, int *wcount, int *timeout); + int (*mod_check_wait_objs)(struct vnc *v); + int (*mod_frame_ack)(struct vnc *v, int flags, int frame_id); + int (*mod_suppress_output)(struct vnc *v, int suppress, + int left, int top, int right, int bottom); + tintptr mod_dumby[100 - 11]; /* align, 100 minus the number of mod functions above */ - /* server functions */ - int (*server_begin_update)(struct vnc* v); - int (*server_end_update)(struct vnc* v); - int (*server_fill_rect)(struct vnc* v, int x, int y, int cx, int cy); - int (*server_screen_blt)(struct vnc* v, int x, int y, int cx, int cy, - int srcx, int srcy); - int (*server_paint_rect)(struct vnc* v, int x, int y, int cx, int cy, - char* data, int width, int height, int srcx, int srcy); - int (*server_set_cursor)(struct vnc* v, int x, int y, char* data, char* mask); - int (*server_palette)(struct vnc* v, int* palette); - int (*server_msg)(struct vnc* v, const char *msg, int code); - int (*server_is_term)(struct vnc* v); - int (*server_set_clip)(struct vnc* v, int x, int y, int cx, int cy); - int (*server_reset_clip)(struct vnc* v); - int (*server_set_fgcolor)(struct vnc* v, int fgcolor); - int (*server_set_bgcolor)(struct vnc* v, int bgcolor); - int (*server_set_opcode)(struct vnc* v, int opcode); - int (*server_set_mixmode)(struct vnc* v, int mixmode); - int (*server_set_brush)(struct vnc* v, int x_origin, int y_origin, - int style, char* pattern); - int (*server_set_pen)(struct vnc* v, int style, - int width); - int (*server_draw_line)(struct vnc* v, int x1, int y1, int x2, int y2); - int (*server_add_char)(struct vnc* v, int font, int character, - int offset, int baseline, - int width, int height, char* data); - int (*server_draw_text)(struct vnc* v, int font, - int flags, int mixmode, int clip_left, int clip_top, - int clip_right, int clip_bottom, - int box_left, int box_top, - int box_right, int box_bottom, - int x, int y, char* data, int data_len); - int (*server_reset)(struct vnc* v, int width, int height, int bpp); - int (*server_query_channel)(struct vnc* v, int index, - char* channel_name, - int* channel_flags); - int (*server_get_channel_id)(struct vnc* v, const char *name); - int (*server_send_to_channel)(struct vnc* v, int channel_id, - char* data, int data_len, - int total_data_len, int flags); - int (*server_bell_trigger)(struct vnc* v); - tintptr server_dumby[100 - 25]; /* align, 100 minus the number of server + /* server functions */ + int (*server_begin_update)(struct vnc *v); + int (*server_end_update)(struct vnc *v); + int (*server_fill_rect)(struct vnc *v, int x, int y, int cx, int cy); + int (*server_screen_blt)(struct vnc *v, int x, int y, int cx, int cy, + int srcx, int srcy); + int (*server_paint_rect)(struct vnc *v, int x, int y, int cx, int cy, + char *data, int width, int height, int srcx, int srcy); + int (*server_set_cursor)(struct vnc *v, int x, int y, char *data, char *mask); + int (*server_palette)(struct vnc *v, int *palette); + int (*server_msg)(struct vnc *v, const char *msg, int code); + int (*server_is_term)(struct vnc *v); + int (*server_set_clip)(struct vnc *v, int x, int y, int cx, int cy); + int (*server_reset_clip)(struct vnc *v); + int (*server_set_fgcolor)(struct vnc *v, int fgcolor); + int (*server_set_bgcolor)(struct vnc *v, int bgcolor); + int (*server_set_opcode)(struct vnc *v, int opcode); + int (*server_set_mixmode)(struct vnc *v, int mixmode); + int (*server_set_brush)(struct vnc *v, int x_origin, int y_origin, + int style, char *pattern); + int (*server_set_pen)(struct vnc *v, int style, + int width); + int (*server_draw_line)(struct vnc *v, int x1, int y1, int x2, int y2); + int (*server_add_char)(struct vnc *v, int font, int character, + int offset, int baseline, + int width, int height, char *data); + int (*server_draw_text)(struct vnc *v, int font, + int flags, int mixmode, int clip_left, int clip_top, + int clip_right, int clip_bottom, + int box_left, int box_top, + int box_right, int box_bottom, + int x, int y, char *data, int data_len); + int (*server_reset)(struct vnc *v, int width, int height, int bpp); + int (*server_query_channel)(struct vnc *v, int index, + char *channel_name, + int *channel_flags); + int (*server_get_channel_id)(struct vnc *v, const char *name); + int (*server_send_to_channel)(struct vnc *v, int channel_id, + char *data, int data_len, + int total_data_len, int flags); + int (*server_bell_trigger)(struct vnc *v); + tintptr server_dumby[100 - 25]; /* align, 100 minus the number of server functions above */ - /* common */ - tintptr handle; /* pointer to self as long */ - tintptr wm; - tintptr painter; - tintptr si; - /* mod data */ - int server_width; - int server_height; - int server_bpp; - int mod_width; - int mod_height; - int mod_bpp; - char mod_name[256]; - int mod_mouse_state; - int palette[256]; - int vnc_desktop; - char username[256]; - char password[256]; - char ip[256]; - char port[256]; - int sck_closed; - int shift_state; /* 0 up, 1 down */ - int keylayout; - int clip_chanid; - struct stream *clip_data_s; - int delay_ms; - struct trans *trans; - int got_guid; - tui8 guid[16]; - int suppress_output; + /* common */ + tintptr handle; /* pointer to self as long */ + tintptr wm; + tintptr painter; + tintptr si; + /* mod data */ + int server_width; + int server_height; + int server_bpp; + char mod_name[256]; + int mod_mouse_state; + int palette[256]; + int vnc_desktop; + char username[256]; + char password[256]; + char ip[256]; + char port[256]; + int sck_closed; + int shift_state; /* 0 up, 1 down */ + int keylayout; + int clip_chanid; + struct stream *clip_data_s; + int delay_ms; + struct trans *trans; + int got_guid; + tui8 guid[16]; + int suppress_output; + unsigned int enabled_encodings_mask; + /* Resizeable support */ + struct vnc_screen_layout client_layout; + enum vnc_resize_status initial_resize_status; }; diff --git a/xrdp/xrdp.ini.in b/xrdp/xrdp.ini.in index 44fd5403..f882afd9 100644 --- a/xrdp/xrdp.ini.in +++ b/xrdp/xrdp.ini.in @@ -196,6 +196,10 @@ ip=127.0.0.1 port=-1 #xserverbpp=24 #delay_ms=2000 +; Disable requested encodings to support buggy VNC servers +; (1 = ExtendedDesktopSize) +#disabled_encodings_mask=0 + [vnc-any] name=vnc-any diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c index 64c75605..28f62d82 100644 --- a/xrdp/xrdp_mm.c +++ b/xrdp/xrdp_mm.c @@ -3261,6 +3261,9 @@ server_draw_text(struct xrdp_mod *mod, int font, } /*****************************************************************************/ + +/* Note : if this is called on a multimon setup, the client is resized + * to a single monitor */ int server_reset(struct xrdp_mod *mod, int width, int height, int bpp) { @@ -3279,10 +3282,11 @@ server_reset(struct xrdp_mod *mod, int width, int height, int bpp) return 0; } - /* if same, don't need to do anything */ + /* if same (and only one monitor on client) don't need to do anything */ if (wm->client_info->width == width && - wm->client_info->height == height && - wm->client_info->bpp == bpp) + wm->client_info->height == height && + wm->client_info->bpp == bpp && + (wm->client_info->monitorCount == 0 || wm->client_info->multimon == 0)) { return 0; } From a2ca01fdf506548ae7b088b5d0ca3353ff8e4d1c Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 10 Apr 2020 10:54:29 +0100 Subject: [PATCH 12/29] Minor manpage fixes --- docs/man/sesman.ini.5.in | 9 ++++++--- docs/man/xrdp-dis.1.in | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/man/sesman.ini.5.in b/docs/man/sesman.ini.5.in index 58bb74b5..6506a7ad 100644 --- a/docs/man/sesman.ini.5.in +++ b/docs/man/sesman.ini.5.in @@ -133,18 +133,21 @@ Sets the maximum number of simultaneous sessions. If not set or set to \fBKillDisconnected\fR=\fI[true|false]\fR If set to \fB1\fR, \fBtrue\fR or \fByes\fR, every session will be killed within 60 seconds after the user disconnects. +This setting currently only works with xorgxrdp sessions. .TP \fBDisconnectedTimeLimit\fR=\fInumber\fR -Sets the time limit (in seconds) before a disconnected session is killed. -If set to \fI0\fR, automatic killing is disabled. +Sets the time limit for \fBKillDisconnected\fR to a value greater than 60. +Values less than 60 cannot be set. You do not actually have to specify +\fBKillDisconnected\fR if you set this value. +This setting currently only works with xorgxrdp sessions. .TP \fBIdleTimeLimit\fR=\fInumber\fR Sets the time limit (in seconds) before an idle session is disconnected. Idle means no keyboard inputs and no mouse moves/clicks here. If set to \fI0\fR, idle sessions will never be disconnected by timeout. -This works only with xorgxrdp session. Moreover, xorgxrdp must be v0.2.9 or later. +This works only with xorgxrdp sessions. Moreover, xorgxrdp must be v0.2.9 or later. .TP \fBPolicy\fR=\fI[Default|UBD|UBI|UBC|UBDI|UBDC]\fR diff --git a/docs/man/xrdp-dis.1.in b/docs/man/xrdp-dis.1.in index 2015389f..a35e441e 100644 --- a/docs/man/xrdp-dis.1.in +++ b/docs/man/xrdp-dis.1.in @@ -17,11 +17,11 @@ to get the default host and display number. .SH FILES .TP .I @socketdir@/xrdp_disconnect_display_* -UNIX socket used to communicate with the \fBxrdp\fP(8) session manager. +UNIX socket used to communicate the disconnect request to xorgxrdp. .SH KNOWN ISSUES .TP -This utility doesn't support disconnecting xorgxrdp sessions so far. +This utility doesn't support disconnecting Xvnc sessions so far. .SH SEE ALSO .BR xrdp (8). From a57c68f55ee5e3be19f04f6b3723f1d0230ed6e0 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:06:41 +0100 Subject: [PATCH 13/29] Implement changes to support cppcheck 2.x --- .travis.yml | 11 ++++---- scripts/install_cppcheck.sh | 54 ++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 255612bf..963f0f38 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ addons: # This is required to use a version of cppcheck other than that # suplied with the operating system cppcheck_defs: &cppcheck_defs - - CPPCHECK_VER=1.90 + - CPPCHECK_VER=2.1 - CPPCHECK_REPO=https://github.com/danmar/cppcheck.git min_amd64_deps: &min_amd64_deps @@ -92,10 +92,11 @@ cppcheck_conf: &cppcheck_conf env: - *cppcheck_defs -# addons: -# apt: -# packages: -# - cppcheck + addons: + apt: + packages: + - libz3-dev + - z3 script: - ./bootstrap diff --git a/scripts/install_cppcheck.sh b/scripts/install_cppcheck.sh index 87f436fb..ef6eecd4 100755 --- a/scripts/install_cppcheck.sh +++ b/scripts/install_cppcheck.sh @@ -48,6 +48,45 @@ call_make() return $status } + +# ---------------------------------------------------------------------------- +# C R E A T E Z 3 V E R S I O N H +# +# Older versions of libz3-dev do not come packaged with z3_version.h. This +# function uses the z3 command to create a copy of this file in the +# cppcheck i#externalsi# directory. +# ---------------------------------------------------------------------------- +create_z3_version_h() +{ + set -- `z3 --version` + if [ $# != 3 -o "$1/$2" != Z3/version ]; then + echo "** Unexpected output from z3 command '$*'" >&2 + false + else + z3ver=$3 ; # e.g. 4.4.3 + set -- `echo $z3ver | tr '.' ' '` + if [ $# != 3 ]; then + echo "** Unable to determine Z3 version from '$z3ver'" >&2 + false + else + { + echo "#ifndef Z3_MAJOR_VERSION" + echo "#define Z3_MAJOR_VERSION $1" + echo "#endif" + echo + echo "#ifndef Z3_MINOR_VERSION" + echo "#define Z3_MINOR_VERSION $2" + echo "#endif" + echo + echo "#ifndef Z3_BUILD_NUMBER" + echo "#define Z3_BUILD_NUMBER $3" + echo "#endif" + } >externals/z3_version.h + echo " - Created z3_version.h for $1.$2.$3" + fi + fi +} + # ---------------------------------------------------------------------------- # M A I N # ---------------------------------------------------------------------------- @@ -78,14 +117,23 @@ fi # Put everything in this directory FILESDIR=$INSTALL_ROOT/$CPPCHECK_VER - # CFGDIR is needed for cppcheck before 1.86 - make_args="FILESDIR=$FILESDIR PREFIX=$FILESDIR CFGDIR=$FILESDIR" - # See https://stackoverflow.com/questions/ # 791959/download-a-specific-tag-with-git git clone -b $CPPCHECK_VER --depth 1 $REPO_URL $workdir cd $workdir + + case "$CPPCHECK_VER" in + 1.*) + # CFGDIR is needed for cppcheck before 1.86 + make_args="FILESDIR=$FILESDIR PREFIX=$FILESDIR CFGDIR=$FILESDIR" + ;; + *) make_args="FILESDIR=$FILESDIR PREFIX=$FILESDIR USE_Z3=yes" + if [ ! -f /usr/include/z3_version.h ]; then + create_z3_version_h + fi + esac + echo "Making cppcheck..." # CFGDIR is needed for cppcheck before 1.86 call_make $make_args From 27653f97f219441466d3e99a1b5f0af8a2f5b08e Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 19 Jun 2020 11:07:23 +0100 Subject: [PATCH 14/29] Code changes for cppcheck 2.1 warnings --- common/log.c | 1 + common/os_calls.c | 6 ++++++ common/pixman-region.c | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/common/log.c b/common/log.c index 50a44e9e..4a0bd2f3 100644 --- a/common/log.c +++ b/common/log.c @@ -569,6 +569,7 @@ log_message(const enum logLevels lvl, const char *msg, ...) if (len > LOG_BUFFER_SIZE) { log_message(LOG_LEVEL_WARNING, "next message will be truncated"); + len = LOG_BUFFER_SIZE; } /* forcing the end of message string */ diff --git a/common/os_calls.c b/common/os_calls.c index 88b96e61..463af84c 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -2896,6 +2896,12 @@ g_strtrim(char *str, int trim_flags) text = (wchar_t *)malloc(len * sizeof(wchar_t) + 8); text1 = (wchar_t *)malloc(len * sizeof(wchar_t) + 8); + if (text == NULL || text1 == NULL) + { + free(text); + free(text1); + return 1; + } text1_index = 0; mbstowcs(text, str, len + 1); diff --git a/common/pixman-region.c b/common/pixman-region.c index eb3fd179..f974eb28 100644 --- a/common/pixman-region.c +++ b/common/pixman-region.c @@ -83,6 +83,12 @@ #define GOOD_RECT(rect) ((rect)->x1 < (rect)->x2 && (rect)->y1 < (rect)->y2) #define BAD_RECT(rect) ((rect)->x1 > (rect)->x2 || (rect)->y1 > (rect)->y2) +/* This file is included by pixman-region16.c which defines PREFIX(x). + * This check allows cppcheck 2.x to scan this file separately */ +#ifndef PREFIX +#define PREFIX(x) pixman_region##x +#endif + #ifdef XRDP_DEBUG #define GOOD(reg) \ From 0505d2813871089cf957757df407fca286652cd5 Mon Sep 17 00:00:00 2001 From: metalefty Date: Thu, 25 Jun 2020 17:18:15 +0900 Subject: [PATCH 15/29] Create SECURITY.md --- SECURITY.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..ae407850 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security Policy + + +## Reporting a Vulnerability + +Please DO NOT report any security issues to public GitHub issue. + +If you find a security vulnerability please kindly inform us about the problem immediately +so that we can fix the security problem to protect a lot of users around the world as soon +as possible. + +Our email eddress for security report is: + +* [xrdp-core@googlegroups.com](mailto:xrdp-core@googlegroups.com) From e593f58a82bf79b556601ae08e9e25e366a662fb Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 12 Jun 2020 09:56:47 +0100 Subject: [PATCH 16/29] Fix for CVE-2020-4044 Reported by: Ashley Newson --- sesman/libscp/libscp_types.h | 4 + sesman/libscp/libscp_v0.c | 337 +++++++++++++++++++++------------ sesman/libscp/libscp_v1s.c | 336 ++++++++++++++++++++++---------- sesman/libscp/libscp_v1s_mng.c | 166 ++++++++++++---- sesman/scp.c | 12 +- 5 files changed, 594 insertions(+), 261 deletions(-) diff --git a/sesman/libscp/libscp_types.h b/sesman/libscp/libscp_types.h index 8cb9166c..84e6c465 100644 --- a/sesman/libscp/libscp_types.h +++ b/sesman/libscp/libscp_types.h @@ -59,6 +59,10 @@ #include "libscp_types_mng.h" +/* Max server incoming and outgoing message size, used to stop memory + exhaustion attempts (CVE-2020-4044) */ +#define SCP_MAX_MESSAGE_SIZE 8192 + struct SCP_CONNECTION { int in_sck; diff --git a/sesman/libscp/libscp_v0.c b/sesman/libscp/libscp_v0.c index 61bf4fda..55168b4f 100644 --- a/sesman/libscp/libscp_v0.c +++ b/sesman/libscp/libscp_v0.c @@ -34,6 +34,65 @@ extern struct log_config *s_log; +/** Maximum length of a string (two bytes + len), excluding the terminator + * + * Practially this is limited by [MS-RDPBCGR] TS_INFO_PACKET + * */ +#define STRING16_MAX_LEN 512 + +/** + * Reads a big-endian uint16 followed by a string into a buffer + * + * Buffer is null-terminated on success + * + * @param s Input stream + * @param [out] Output buffer (must be >= (STRING16_MAX_LEN+1) chars) + * @param param Parameter we're reading + * @param line Line number reference + * @return != 0 if string read OK + */ +static +int in_string16(struct stream *s, char str[], const char *param, int line) +{ + int result; + + if (!s_check_rem(s, 2)) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: %s len missing", + line, param); + result = 0; + } + else + { + unsigned int sz; + + in_uint16_be(s, sz); + if (sz > STRING16_MAX_LEN) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: %s too long (%u chars)", + line, param, sz); + result = 0; + } + else + { + result = s_check_rem(s, sz); + if (!result) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: %s data missing", + line, param); + } + else + { + in_uint8a(s, str, sz); + str[sz] = '\0'; + } + } + } + return result; +} /* client API */ /******************************************************************************/ enum SCP_CLIENT_STATES_E @@ -71,10 +130,24 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) } sz = g_strlen(s->username); + if (sz > STRING16_MAX_LEN) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: username too long", + __LINE__); + return SCP_CLIENT_STATE_SIZE_ERR; + } out_uint16_be(c->out_s, sz); out_uint8a(c->out_s, s->username, sz); sz = g_strlen(s->password); + if (sz > STRING16_MAX_LEN) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: password too long", + __LINE__); + return SCP_CLIENT_STATE_SIZE_ERR; + } out_uint16_be(c->out_s, sz); out_uint8a(c->out_s, s->password, sz); out_uint16_be(c->out_s, s->width); @@ -111,14 +184,16 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) in_uint32_be(c->in_s, size); - if (size < 14) + if (size < (8 + 2 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { - log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: packet size error", __LINE__); + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: msg size = %u", + __LINE__, (unsigned int)size); return SCP_CLIENT_STATE_SIZE_ERR; } /* getting payload */ - init_stream(c->in_s, c->in_s->size); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, size - 8)) { @@ -126,6 +201,8 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) return SCP_CLIENT_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + /* check code */ in_uint16_be(c->in_s, sz); @@ -151,43 +228,38 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) return SCP_CLIENT_STATE_END; } -/* server API */ -/******************************************************************************/ -enum SCP_SERVER_STATES_E -scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) +/** + * Initialises a V0 session object + * + * At the time of the call, the version has been read from the connection + * + * @param c Connection + * @param [out] session pre-allocated session object + * @return SCP_SERVER_STATE_OK for success + */ +static enum SCP_SERVER_STATES_E +scp_v0s_init_session(struct SCP_CONNECTION *c, struct SCP_SESSION *session) { - tui32 version = 0; tui32 size; - struct SCP_SESSION *session = 0; - tui16 sz; + tui16 height; + tui16 width; + tui16 bpp; tui32 code = 0; - char *buf = 0; + char buf[STRING16_MAX_LEN + 1]; - if (!skipVchk) + scp_session_set_version(session, 0); + + /* Check for a header and a code value in the length */ + in_uint32_be(c->in_s, size); + if (size < (8 + 2) || size > SCP_MAX_MESSAGE_SIZE) { - LOG_DBG("[v0:%d] starting connection", __LINE__); - - if (0 == scp_tcp_force_recv(c->in_sck, c->in_s->data, 8)) - { - c->in_s->end = c->in_s->data + 8; - in_uint32_be(c->in_s, version); - - if (version != 0) - { - log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: version error", __LINE__); - return SCP_SERVER_STATE_VERSION_ERR; - } - } - else - { - log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: network error", __LINE__); - return SCP_SERVER_STATE_NETWORK_ERR; - } + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: msg size = %u", + __LINE__, (unsigned int)size); + return SCP_SERVER_STATE_SIZE_ERR; } - in_uint32_be(c->in_s, size); - - init_stream(c->in_s, 8196); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, size - 8)) { @@ -201,16 +273,6 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) if (code == 0 || code == 10 || code == 20) { - session = scp_session_create(); - - if (0 == session) - { - log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: network error", __LINE__); - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - scp_session_set_version(session, version); - if (code == 0) { scp_session_set_type(session, SCP_SESSION_TYPE_XVNC); @@ -225,154 +287,130 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) } /* reading username */ - in_uint16_be(c->in_s, sz); - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; + if (!in_string16(c->in_s, buf, "username", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_username(session, buf)) { - scp_session_destroy(session); log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: error setting username", __LINE__); - g_free(buf); return SCP_SERVER_STATE_INTERNAL_ERR; } - g_free(buf); /* reading password */ - in_uint16_be(c->in_s, sz); - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; + if (!in_string16(c->in_s, buf, "passwd", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_password(session, buf)) { - scp_session_destroy(session); log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: error setting password", __LINE__); - g_free(buf); return SCP_SERVER_STATE_INTERNAL_ERR; } - g_free(buf); - /* width */ - in_uint16_be(c->in_s, sz); - scp_session_set_width(session, sz); - /* height */ - in_uint16_be(c->in_s, sz); - scp_session_set_height(session, sz); - /* bpp */ - in_uint16_be(c->in_s, sz); - if (0 != scp_session_set_bpp(session, (tui8)sz)) + /* width + height + bpp */ + if (!s_check_rem(c->in_s, 2 + 2 + 2)) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: width+height+bpp missing", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + in_uint16_be(c->in_s, width); + scp_session_set_width(session, width); + in_uint16_be(c->in_s, height); + scp_session_set_height(session, height); + in_uint16_be(c->in_s, bpp); + if (0 != scp_session_set_bpp(session, (tui8)bpp)) { - scp_session_destroy(session); log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: unsupported bpp: %d", - __LINE__, (tui8)sz); + __LINE__, (tui8)bpp); return SCP_SERVER_STATE_INTERNAL_ERR; } if (s_check_rem(c->in_s, 2)) { /* reading domain */ - in_uint16_be(c->in_s, sz); - - if (sz > 0) + if (!in_string16(c->in_s, buf, "domain", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + if (buf[0] != '\0') { - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; scp_session_set_domain(session, buf); - g_free(buf); } } if (s_check_rem(c->in_s, 2)) { /* reading program */ - in_uint16_be(c->in_s, sz); - - if (sz > 0) + if (!in_string16(c->in_s, buf, "program", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + + if (buf[0] != '\0') { - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; scp_session_set_program(session, buf); - g_free(buf); } } if (s_check_rem(c->in_s, 2)) { /* reading directory */ - in_uint16_be(c->in_s, sz); - - if (sz > 0) + if (!in_string16(c->in_s, buf, "directory", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + + if (buf[0] != '\0') { - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; scp_session_set_directory(session, buf); - g_free(buf); } } if (s_check_rem(c->in_s, 2)) { /* reading client IP address */ - in_uint16_be(c->in_s, sz); - - if (sz > 0) + if (!in_string16(c->in_s, buf, "client IP", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + if (buf[0] != '\0') { - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; scp_session_set_client_ip(session, buf); - g_free(buf); } } } else if (code == SCP_GW_AUTHENTICATION) { - /* g_writeln("Command is SCP_GW_AUTHENTICATION"); */ - session = scp_session_create(); - - if (0 == session) - { - /* until syslog merge log_message(s_log, LOG_LEVEL_WARNING, "[v0:%d] connection aborted: network error", __LINE__);*/ - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - scp_session_set_version(session, version); scp_session_set_type(session, SCP_GW_AUTHENTICATION); /* reading username */ - in_uint16_be(c->in_s, sz); - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; + if (!in_string16(c->in_s, buf, "username", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } /* g_writeln("Received user name: %s",buf); */ if (0 != scp_session_set_username(session, buf)) { - scp_session_destroy(session); /* until syslog merge log_message(s_log, LOG_LEVEL_WARNING, "[v0:%d] connection aborted: error setting username", __LINE__);*/ - g_free(buf); return SCP_SERVER_STATE_INTERNAL_ERR; } - g_free(buf); /* reading password */ - in_uint16_be(c->in_s, sz); - buf = g_new0(char, sz + 1); - in_uint8a(c->in_s, buf, sz); - buf[sz] = '\0'; + if (!in_string16(c->in_s, buf, "passwd", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } /* g_writeln("Received password: %s",buf); */ if (0 != scp_session_set_password(session, buf)) { - scp_session_destroy(session); /* until syslog merge log_message(s_log, LOG_LEVEL_WARNING, "[v0:%d] connection aborted: error setting password", __LINE__); */ - g_free(buf); return SCP_SERVER_STATE_INTERNAL_ERR; } - g_free(buf); } else { @@ -380,10 +418,67 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) return SCP_SERVER_STATE_SEQUENCE_ERR; } - (*s) = session; return SCP_SERVER_STATE_OK; } + +/* server API */ +/******************************************************************************/ +enum SCP_SERVER_STATES_E +scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) +{ + enum SCP_SERVER_STATES_E result = SCP_SERVER_STATE_OK; + struct SCP_SESSION *session = NULL; + tui32 version = 0; + + if (!skipVchk) + { + LOG_DBG("[v0:%d] starting connection", __LINE__); + + if (0 == scp_tcp_force_recv(c->in_sck, c->in_s->data, 8)) + { + c->in_s->end = c->in_s->data + 8; + in_uint32_be(c->in_s, version); + + if (version != 0) + { + log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: version error", __LINE__); + result = SCP_SERVER_STATE_VERSION_ERR; + } + } + else + { + log_message(LOG_LEVEL_WARNING, "[v0:%d] connection aborted: network error", __LINE__); + result = SCP_SERVER_STATE_NETWORK_ERR; + } + } + + if (result == SCP_SERVER_STATE_OK) + { + session = scp_session_create(); + if (NULL == session) + { + log_message(LOG_LEVEL_WARNING, + "[v0:%d] connection aborted: no memory", + __LINE__); + result = SCP_SERVER_STATE_INTERNAL_ERR; + } + else + { + result = scp_v0s_init_session(c, session); + if (result != SCP_SERVER_STATE_OK) + { + scp_session_destroy(session); + session = NULL; + } + } + } + + (*s) = session; + + return result; +} + /******************************************************************************/ enum SCP_SERVER_STATES_E scp_v0s_allow_connection(struct SCP_CONNECTION *c, SCP_DISPLAY d, const tui8 *guid) diff --git a/sesman/libscp/libscp_v1s.c b/sesman/libscp/libscp_v1s.c index 27e5ef13..37e8a01c 100644 --- a/sesman/libscp/libscp_v1s.c +++ b/sesman/libscp/libscp_v1s.c @@ -35,16 +35,194 @@ //extern struct log_config* s_log; +/** + * Reads a uint8 followed by a string into a buffer + * + * Buffer is null-terminated on success + * + * @param s Input stream + * @param [out] Output buffer (must be >= 256 chars) + * @param param Parameter we're reading + * @param line Line number reference + * @return != 0 if string read OK + * + * @todo + * This needs to be merged with the func of the same name in + * libscp_v1s_mng.c + */ +static +int in_string8(struct stream *s, char str[], const char *param, int line) +{ + int result; + + if (!s_check_rem(s, 1)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: %s len missing", + line, param); + result = 0; + } + else + { + unsigned int sz; + + in_uint8(s, sz); + result = s_check_rem(s, sz); + if (!result) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: %s data missing", + line, param); + } + else + { + in_uint8a(s, str, sz); + str[sz] = '\0'; + } + } + return result; +} +/* server API */ + +/** + * Initialises a V1 session object + * + * This is called after the V1 header, command set and command have been read + * + * @param c Connection + * @param [out] session pre-allocated session object + * @return SCP_SERVER_STATE_OK for success + */ +static enum SCP_SERVER_STATES_E +scp_v1s_init_session(struct SCP_CONNECTION *c, struct SCP_SESSION *session) +{ + tui8 type; + tui16 height; + tui16 width; + tui8 bpp; + tui8 sz; + char buf[256]; + + scp_session_set_version(session, 1); + + /* Check there's data for the session type, the height, the width, the + * bpp, the resource sharing indicator and the locale */ + if (!s_check_rem(c->in_s, 1 + 2 + 2 + 1 + 1 + 17)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: short packet", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + + in_uint8(c->in_s, type); + + if ((type != SCP_SESSION_TYPE_XVNC) && (type != SCP_SESSION_TYPE_XRDP)) + { + log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: unknown session type", __LINE__); + return SCP_SERVER_STATE_SESSION_TYPE_ERR; + } + + scp_session_set_type(session, type); + + in_uint16_be(c->in_s, height); + scp_session_set_height(session, height); + in_uint16_be(c->in_s, width); + scp_session_set_width(session, width); + in_uint8(c->in_s, bpp); + if (0 != scp_session_set_bpp(session, bpp)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: unsupported bpp: %d", + __LINE__, bpp); + return SCP_SERVER_STATE_INTERNAL_ERR; + } + in_uint8(c->in_s, sz); + scp_session_set_rsr(session, sz); + in_uint8a(c->in_s, buf, 17); + buf[17] = '\0'; + scp_session_set_locale(session, buf); + + /* Check there's enough data left for at least an IPv4 address (+len) */ + if (!s_check_rem(c->in_s, 1 + 4)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: IP addr len missing", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + + in_uint8(c->in_s, sz); + + if (sz == SCP_ADDRESS_TYPE_IPV4) + { + tui32 ipv4; + in_uint32_be(c->in_s, ipv4); + scp_session_set_addr(session, sz, &ipv4); + } + else if (sz == SCP_ADDRESS_TYPE_IPV6) + { + if (!s_check_rem(c->in_s, 16)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: IP addr missing", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + in_uint8a(c->in_s, buf, 16); + scp_session_set_addr(session, sz, buf); + } + + /* reading hostname */ + if (!in_string8(c->in_s, buf, "hostname", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + + if (0 != scp_session_set_hostname(session, buf)) + { + log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); + return SCP_SERVER_STATE_INTERNAL_ERR; + } + + /* reading username */ + if (!in_string8(c->in_s, buf, "username", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + + if (0 != scp_session_set_username(session, buf)) + { + log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); + return SCP_SERVER_STATE_INTERNAL_ERR; + } + + /* reading password */ + if (!in_string8(c->in_s, buf, "passwd", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } + + if (0 != scp_session_set_password(session, buf)) + { + log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); + return SCP_SERVER_STATE_INTERNAL_ERR; + } + + return SCP_SERVER_STATE_OK; +} + /* server API */ enum SCP_SERVER_STATES_E scp_v1s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk) { + enum SCP_SERVER_STATES_E result; struct SCP_SESSION *session; tui32 version; tui32 size; tui16 cmdset; tui16 cmd; - tui8 sz; - char buf[257]; + + (*s) = NULL; if (!skipVchk) { @@ -68,13 +246,15 @@ enum SCP_SERVER_STATES_E scp_v1s_accept(struct SCP_CONNECTION *c, struct SCP_SES in_uint32_be(c->in_s, size); - if (size < 12) + /* Check the message is big enough for the header, the command set, and + * the command (but not too big) */ + if (size < (8 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: size error", __LINE__); return SCP_SERVER_STATE_SIZE_ERR; } - init_stream(c->in_s, c->in_s->size); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, (size - 8))) { @@ -82,6 +262,8 @@ enum SCP_SERVER_STATES_E scp_v1s_accept(struct SCP_CONNECTION *c, struct SCP_SES return SCP_SERVER_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + /* reading command set */ in_uint16_be(c->in_s, cmdset); @@ -111,98 +293,27 @@ enum SCP_SERVER_STATES_E scp_v1s_accept(struct SCP_CONNECTION *c, struct SCP_SES session = scp_session_create(); - if (0 == session) + if (NULL == session) { - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error (malloc returned NULL)", __LINE__); - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - scp_session_set_version(session, 1); - - in_uint8(c->in_s, sz); - - if ((sz != SCP_SESSION_TYPE_XVNC) && (sz != SCP_SESSION_TYPE_XRDP)) - { - scp_session_destroy(session); - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: unknown session type", __LINE__); - return SCP_SERVER_STATE_SESSION_TYPE_ERR; - } - - scp_session_set_type(session, sz); - - in_uint16_be(c->in_s, cmd); - scp_session_set_height(session, cmd); - in_uint16_be(c->in_s, cmd); - scp_session_set_width(session, cmd); - in_uint8(c->in_s, sz); - if (0 != scp_session_set_bpp(session, sz)) - { - scp_session_destroy(session); log_message(LOG_LEVEL_WARNING, - "[v1s:%d] connection aborted: unsupported bpp: %d", - __LINE__, sz); - return SCP_SERVER_STATE_INTERNAL_ERR; + "[v1s:%d] connection aborted: internal error " + "(malloc returned NULL)", __LINE__); + result = SCP_SERVER_STATE_INTERNAL_ERR; } - in_uint8(c->in_s, sz); - scp_session_set_rsr(session, sz); - in_uint8a(c->in_s, buf, 17); - buf[17] = '\0'; - scp_session_set_locale(session, buf); - - in_uint8(c->in_s, sz); - - if (sz == SCP_ADDRESS_TYPE_IPV4) + else { - in_uint32_be(c->in_s, size); - scp_session_set_addr(session, sz, &size); - } - else if (sz == SCP_ADDRESS_TYPE_IPV6) - { - in_uint8a(c->in_s, buf, 16); - scp_session_set_addr(session, sz, buf); - } - - buf[256] = '\0'; - /* reading hostname */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); - - if (0 != scp_session_set_hostname(session, buf)) - { - scp_session_destroy(session); - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - /* reading username */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); - - if (0 != scp_session_set_username(session, buf)) - { - scp_session_destroy(session); - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - /* reading password */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); - - if (0 != scp_session_set_password(session, buf)) - { - scp_session_destroy(session); - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: internal error", __LINE__); - return SCP_SERVER_STATE_INTERNAL_ERR; + result = scp_v1s_init_session(c, session); + if (result != SCP_SERVER_STATE_OK) + { + scp_session_destroy(session); + session = NULL; + } } /* returning the struct */ (*s) = session; - return SCP_SERVER_STATE_OK; + return result; } enum SCP_SERVER_STATES_E @@ -242,13 +353,12 @@ enum SCP_SERVER_STATES_E scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, const char *reason) { - tui8 sz; tui32 version; tui32 size; tui16 cmdset; tui16 cmd; int rlen; - char buf[257]; + char buf[256]; init_stream(c->in_s, c->in_s->size); init_stream(c->out_s, c->out_s->size); @@ -296,13 +406,17 @@ scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, in_uint32_be(c->in_s, size); - if (size < 12) + /* Check the message is big enough for the header, the command set, and + * the command (but not too big) */ + if (size < (8 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { - log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: size error", __LINE__); + log_message(LOG_LEVEL_WARNING, + "[v1s:%d] connection aborted: size error", + __LINE__); return SCP_SERVER_STATE_SIZE_ERR; } - init_stream(c->in_s, c->in_s->size); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, (size - 8))) { @@ -310,6 +424,8 @@ scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, return SCP_SERVER_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + in_uint16_be(c->in_s, cmdset); if (cmdset != SCP_COMMAND_SET_DEFAULT) @@ -326,11 +442,11 @@ scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, return SCP_SERVER_STATE_SEQUENCE_ERR; } - buf[256] = '\0'; /* reading username */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); + if (!in_string8(c->in_s, buf, "username", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_username(s, buf)) { @@ -339,9 +455,10 @@ scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, } /* reading password */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); + if (!in_string8(c->in_s, buf, "passwd", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_password(s, buf)) { @@ -468,13 +585,15 @@ scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNEC in_uint32_be(c->in_s, size); - if (size < 12) + /* Check the message is big enough for the header, the command set, and + * the command (but not too big) */ + if (size < (8 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: size error", __LINE__); return SCP_SERVER_STATE_SIZE_ERR; } - init_stream(c->in_s, c->in_s->size); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, (size - 8))) { @@ -482,6 +601,8 @@ scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNEC return SCP_SERVER_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + in_uint16_be(c->in_s, cmd); if (cmd != SCP_COMMAND_SET_DEFAULT) @@ -606,14 +727,16 @@ scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNEC in_uint32_be(c->in_s, size); - if (size < 12) + /* Check the message is big enough for the header, the command set, and + * the command (but not too big) */ + if (size < (8 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: size error", __LINE__); return SCP_SERVER_STATE_SIZE_ERR; } /* rest of the packet */ - init_stream(c->in_s, c->in_s->size); + init_stream(c->in_s, size - 8); if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, (size - 8))) { @@ -621,6 +744,8 @@ scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNEC return SCP_SERVER_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + in_uint16_be(c->in_s, cmd); if (cmd != SCP_COMMAND_SET_DEFAULT) @@ -633,6 +758,11 @@ scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNEC if (cmd == 43) { + if (!s_check_rem(c->in_s, 4)) + { + log_message(LOG_LEVEL_WARNING, "[v1s:%d] connection aborted: missing session", __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } /* select session */ in_uint32_be(c->in_s, (*sid)); diff --git a/sesman/libscp/libscp_v1s_mng.c b/sesman/libscp/libscp_v1s_mng.c index 3a3e1d98..8e9aed12 100644 --- a/sesman/libscp/libscp_v1s_mng.c +++ b/sesman/libscp/libscp_v1s_mng.c @@ -38,17 +38,79 @@ static enum SCP_SERVER_STATES_E _scp_v1s_mng_check_response(struct SCP_CONNECTION *c, struct SCP_SESSION *s); -/* server API */ -enum SCP_SERVER_STATES_E -scp_v1s_mng_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s) +/** + * Reads a uint8 followed by a string into a buffer + * + * Buffer is null-terminated on success + * + * @param s Input stream + * @param [out] Output buffer (must be >= 256 chars) + * @param param Parameter we're reading + * @param line Line number reference + * @return != 0 if string read OK + * + * @todo + * This needs to be merged with the func of the same name in + * libscp_v1s.c + */ +static +int in_string8(struct stream *s, char str[], const char *param, int line) +{ + int result; + + if (!s_check_rem(s, 1)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s_mng:%d] connection aborted: %s len missing", + line, param); + result = 0; + } + else + { + unsigned int sz; + + in_uint8(s, sz); + result = s_check_rem(s, sz); + if (!result) + { + log_message(LOG_LEVEL_WARNING, + "[v1s_mng:%d] connection aborted: %s data missing", + line, param); + } + else + { + in_uint8a(s, str, sz); + str[sz] = '\0'; + } + } + return result; +} +/** + * Initialises a V1 management session object + * + * At call time, the command set value has been read from the wire, and + * the command still needs to be processed. + * + * @param c Connection + * @param [out] session pre-allocated session object + * @return SCP_SERVER_STATE_START_MANAGE for success + */ +static enum SCP_SERVER_STATES_E +scp_v1s_mng_init_session(struct SCP_CONNECTION *c, struct SCP_SESSION *session) { - struct SCP_SESSION *session; tui32 ipaddr; tui16 cmd; tui8 sz; - char buf[257]; + char buf[256]; + + scp_session_set_version(session, 1); /* reading command */ + if (!s_check_rem(c->in_s, 2)) + { + /* Caller should have checked this */ + return SCP_SERVER_STATE_SIZE_ERR; + } in_uint16_be(c->in_s, cmd); if (cmd != 1) /* manager login */ @@ -56,41 +118,39 @@ scp_v1s_mng_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s) return SCP_SERVER_STATE_SEQUENCE_ERR; } - session = scp_session_create(); - - if (0 == session) - { - return SCP_SERVER_STATE_INTERNAL_ERR; - } - - scp_session_set_version(session, 1); - scp_session_set_type(session, SCP_SESSION_TYPE_MANAGE); - /* reading username */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); + if (!in_string8(c->in_s, buf, "username", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_username(session, buf)) { - scp_session_destroy(session); return SCP_SERVER_STATE_INTERNAL_ERR; } /* reading password */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); + if (!in_string8(c->in_s, buf, "passwd", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_password(session, buf)) { - scp_session_destroy(session); return SCP_SERVER_STATE_INTERNAL_ERR; } - /* reading remote address */ - in_uint8(c->in_s, sz); + /* reading remote address + * Check there's enough data left for at least an IPv4 address (+len) */ + if (!s_check_rem(c->in_s, 1 + 4)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s_mng:%d] connection aborted: IP addr len missing", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + in_uint8(c->in_s, sz); if (sz == SCP_ADDRESS_TYPE_IPV4) { in_uint32_be(c->in_s, ipaddr); @@ -98,25 +158,57 @@ scp_v1s_mng_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s) } else if (sz == SCP_ADDRESS_TYPE_IPV6) { + if (!s_check_rem(c->in_s, 16)) + { + log_message(LOG_LEVEL_WARNING, + "[v1s_mng:%d] connection aborted: IP addr missing", + __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } in_uint8a(c->in_s, buf, 16); scp_session_set_addr(session, sz, buf); } /* reading hostname */ - in_uint8(c->in_s, sz); - buf[sz] = '\0'; - in_uint8a(c->in_s, buf, sz); + if (!in_string8(c->in_s, buf, "hostname", __LINE__)) + { + return SCP_SERVER_STATE_SIZE_ERR; + } if (0 != scp_session_set_hostname(session, buf)) { - scp_session_destroy(session); return SCP_SERVER_STATE_INTERNAL_ERR; } - /* returning the struct */ + return SCP_SERVER_STATE_START_MANAGE; +} + +enum SCP_SERVER_STATES_E +scp_v1s_mng_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s) +{ + enum SCP_SERVER_STATES_E result; + struct SCP_SESSION *session; + + session = scp_session_create(); + if (NULL == session) + { + result = SCP_SERVER_STATE_INTERNAL_ERR; + } + else + { + scp_session_set_type(session, SCP_SESSION_TYPE_MANAGE); + + result = scp_v1s_mng_init_session(c, session); + if (result != SCP_SERVER_STATE_START_MANAGE) + { + scp_session_destroy(session); + session = NULL; + } + } + (*s) = session; - return SCP_SERVER_STATE_START_MANAGE; + return result; } /* 002 */ @@ -312,7 +404,15 @@ _scp_v1s_mng_check_response(struct SCP_CONNECTION *c, struct SCP_SESSION *s) in_uint32_be(c->in_s, size); - init_stream(c->in_s, c->in_s->size); + /* Check the message is big enough for the header, the command set, and + * the command (but not too big) */ + if (size < (8 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) + { + log_message(LOG_LEVEL_WARNING, "[v1s_mng:%d] connection aborted: size error", __LINE__); + return SCP_SERVER_STATE_SIZE_ERR; + } + + init_stream(c->in_s, size - 8); /* read the rest of the packet */ if (0 != scp_tcp_force_recv(c->in_sck, c->in_s->data, size - 8)) @@ -321,6 +421,8 @@ _scp_v1s_mng_check_response(struct SCP_CONNECTION *c, struct SCP_SESSION *s) return SCP_SERVER_STATE_NETWORK_ERR; } + c->in_s->end = c->in_s->data + (size - 8); + in_uint16_be(c->in_s, cmd); if (cmd != SCP_COMMAND_SET_MANAGE) diff --git a/sesman/scp.c b/sesman/scp.c index d548052a..7fa4d2a7 100644 --- a/sesman/scp.c +++ b/sesman/scp.c @@ -48,8 +48,8 @@ scp_process_start(void *sck) make_stream(scon.in_s); make_stream(scon.out_s); - init_stream(scon.in_s, 8192); - init_stream(scon.out_s, 8192); + init_stream(scon.in_s, SCP_MAX_MESSAGE_SIZE); + init_stream(scon.out_s, SCP_MAX_MESSAGE_SIZE); switch (scp_vXs_accept(&scon, &(sdata))) { @@ -76,10 +76,12 @@ scp_process_start(void *sck) scp_v1_mng_process(&scon, sdata); break; case SCP_SERVER_STATE_VERSION_ERR: - /* an unknown scp version was requested, so we shut down the */ - /* connection (and log the fact) */ + case SCP_SERVER_STATE_SIZE_ERR: + /* an unknown scp version was requested, or the message sizes + are inconsistent. Shut down the connection and log the + fact */ log_message(LOG_LEVEL_WARNING, - "unknown protocol version specified. connection refused."); + "protocol violation. connection refused."); break; case SCP_SERVER_STATE_NETWORK_ERR: log_message(LOG_LEVEL_WARNING, "libscp network error."); From 7384f6e574c348dc4a753ac9245ff25a6014fbe7 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 29 Jun 2020 11:22:13 +0100 Subject: [PATCH 17/29] Fixed CVE-2020-4044 CI errors --- sesman/libscp/libscp_v0.c | 12 ++++++------ sesman/libscp/libscp_v1s.c | 6 +++--- sesman/libscp/libscp_v1s_mng.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sesman/libscp/libscp_v0.c b/sesman/libscp/libscp_v0.c index 55168b4f..90bf921e 100644 --- a/sesman/libscp/libscp_v0.c +++ b/sesman/libscp/libscp_v0.c @@ -99,7 +99,7 @@ enum SCP_CLIENT_STATES_E scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) { tui32 version; - tui32 size; + int size; tui16 sz; init_stream(c->in_s, c->in_s->size); @@ -187,8 +187,8 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) if (size < (8 + 2 + 2 + 2) || size > SCP_MAX_MESSAGE_SIZE) { log_message(LOG_LEVEL_WARNING, - "[v0:%d] connection aborted: msg size = %u", - __LINE__, (unsigned int)size); + "[v0:%d] connection aborted: msg size = %d", + __LINE__, size); return SCP_CLIENT_STATE_SIZE_ERR; } @@ -240,7 +240,7 @@ scp_v0c_connect(struct SCP_CONNECTION *c, struct SCP_SESSION *s) static enum SCP_SERVER_STATES_E scp_v0s_init_session(struct SCP_CONNECTION *c, struct SCP_SESSION *session) { - tui32 size; + int size; tui16 height; tui16 width; tui16 bpp; @@ -254,8 +254,8 @@ scp_v0s_init_session(struct SCP_CONNECTION *c, struct SCP_SESSION *session) if (size < (8 + 2) || size > SCP_MAX_MESSAGE_SIZE) { log_message(LOG_LEVEL_WARNING, - "[v0:%d] connection aborted: msg size = %u", - __LINE__, (unsigned int)size); + "[v0:%d] connection aborted: msg size = %d", + __LINE__, size); return SCP_SERVER_STATE_SIZE_ERR; } diff --git a/sesman/libscp/libscp_v1s.c b/sesman/libscp/libscp_v1s.c index 37e8a01c..7aab200d 100644 --- a/sesman/libscp/libscp_v1s.c +++ b/sesman/libscp/libscp_v1s.c @@ -218,7 +218,7 @@ enum SCP_SERVER_STATES_E scp_v1s_accept(struct SCP_CONNECTION *c, struct SCP_SES enum SCP_SERVER_STATES_E result; struct SCP_SESSION *session; tui32 version; - tui32 size; + int size; tui16 cmdset; tui16 cmd; @@ -354,7 +354,7 @@ scp_v1s_request_password(struct SCP_CONNECTION *c, struct SCP_SESSION *s, const char *reason) { tui32 version; - tui32 size; + int size; tui16 cmdset; tui16 cmd; int rlen; @@ -539,7 +539,7 @@ enum SCP_SERVER_STATES_E scp_v1s_list_sessions(struct SCP_CONNECTION *c, int sescnt, struct SCP_DISCONNECTED_SESSION *ds, SCP_SID *sid) { tui32 version = 1; - tui32 size = 12; + int size = 12; tui16 cmd = 40; int pktcnt; int idx; diff --git a/sesman/libscp/libscp_v1s_mng.c b/sesman/libscp/libscp_v1s_mng.c index 8e9aed12..28c8290f 100644 --- a/sesman/libscp/libscp_v1s_mng.c +++ b/sesman/libscp/libscp_v1s_mng.c @@ -381,7 +381,7 @@ static enum SCP_SERVER_STATES_E _scp_v1s_mng_check_response(struct SCP_CONNECTION *c, struct SCP_SESSION *s) { tui32 version; - tui32 size; + int size; tui16 cmd; // tui8 dim; // char buf[257]; From 7e58209b195052205831d53a9f4cd0b4604a293d Mon Sep 17 00:00:00 2001 From: Alexandre Quesnel <131881+aquesnel@users.noreply.github.com> Date: Wed, 22 Jul 2020 03:03:09 +0000 Subject: [PATCH 18/29] Fixing the build with --enable-xrdpdebug and CFLAGS=-Werror -Wall --- .travis.yml | 17 ++++++++++++++++- common/pixman-region.c | 2 ++ configure.ac | 23 +++++++++++++---------- mc/mc.h | 2 +- sesman/access.c | 5 ++--- sesman/scp_v1.c | 2 +- sesman/scp_v1_mng.c | 2 +- 7 files changed, 36 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 963f0f38..45332ed5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,6 +53,17 @@ max_amd64_conf: &max_amd64_conf packages: - *max_amd64_deps +max_debug_amd64_conf: &max_debug_amd64_conf + env: + - CONF_FLAGS="--enable-ipv6 --enable-jpeg --enable-fuse --enable-mp3lame + --enable-fdkaac --enable-opus --enable-rfxcodec --enable-painter + --enable-pixman --enable-xrdpdebug" + - DISTCHECK=1 + addons: + apt: + packages: + - *max_amd64_deps + max_x86_deps: &max_x86_deps - *common_deps - g++-multilib @@ -123,6 +134,10 @@ matrix: - compiler: clang << : *max_amd64_conf + # Maximal debug amd64 build + - compiler: gcc + << : *max_debug_amd64_conf + # Maximal x86 build - compiler: gcc << : *max_x86_conf @@ -139,5 +154,5 @@ matrix: script: - ./bootstrap - ./configure $CONF_FLAGS - - make CFLAGS="$CFLAGS -O2 -Wall -Wwrite-strings -Werror" + - make CFLAGS="$CFLAGS" - test -z "$DISTCHECK" || make distcheck diff --git a/common/pixman-region.c b/common/pixman-region.c index f974eb28..ccdea9ad 100644 --- a/common/pixman-region.c +++ b/common/pixman-region.c @@ -91,6 +91,8 @@ #ifdef XRDP_DEBUG +pixman_bool_t PREFIX(_selfcheck) (region_type_t *reg); + #define GOOD(reg) \ do \ { \ diff --git a/configure.ac b/configure.ac index c64867ad..d5fc41bc 100644 --- a/configure.ac +++ b/configure.ac @@ -19,11 +19,6 @@ AC_CONFIG_SUBDIRS([libpainter librfxcodec]) # Use silent rules by default if supported by Automake m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) -AX_CFLAGS_WARN_ALL -AX_APPEND_COMPILE_FLAGS([-Wwrite-strings]) -AX_GCC_FUNC_ATTRIBUTE([format]) -AX_TYPE_SOCKLEN_T - case $host_os in *linux*) linux=yes @@ -155,6 +150,19 @@ AC_ARG_ENABLE(rdpsndaudin, AS_HELP_STRING([--enable-rdpsndaudin], [], [enable_rdpsndaudin=no]) AM_CONDITIONAL(XRDP_RDPSNDAUDIN, [test x$enable_rdpsndaudin = xyes]) +# configure compiler options and CFLAGS +AX_GCC_FUNC_ATTRIBUTE([format]) +AX_TYPE_SOCKLEN_T +AX_CFLAGS_WARN_ALL +AX_APPEND_COMPILE_FLAGS([-Wwrite-strings]) + +AM_COND_IF([LINUX], + [AX_APPEND_COMPILE_FLAGS([-Werror])]) # bsd has warnings that have not been fixed yet + +AM_COND_IF([XRDP_DEBUG], + [AX_APPEND_COMPILE_FLAGS([-g -O0])], + [AX_APPEND_COMPILE_FLAGS([-O2])]) + # Don't fail without working nasm if rfxcodec is not enabled if test "x$enable_rfxcodec" != xyes; then with_simd=no @@ -243,11 +251,6 @@ then [AC_MSG_ERROR([please install libjpeg-dev or libjpeg-devel])]) fi -if test "x$enable_xrdpdebug" = "xyes" -then - CFLAGS="-g -O0" -fi - # checking for fuse if test "x$enable_fuse" = "xyes" then diff --git a/mc/mc.h b/mc/mc.h index 319f1a4c..7949695d 100644 --- a/mc/mc.h +++ b/mc/mc.h @@ -54,7 +54,7 @@ struct mod char* data, int width, int height, int srcx, int srcy); int (*server_set_cursor)(struct mod* v, int x, int y, char* data, char* mask); int (*server_palette)(struct mod* v, int* palette); - int (*server_msg)(struct mod* v, char* msg, int code); + int (*server_msg)(struct mod* v, const char* msg, int code); int (*server_is_term)(struct mod* v); int (*server_set_clip)(struct mod* v, int x, int y, int cx, int cy); int (*server_reset_clip)(struct mod* v); diff --git a/sesman/access.c b/sesman/access.c index b978bf28..2ff7c9c5 100644 --- a/sesman/access.c +++ b/sesman/access.c @@ -48,8 +48,7 @@ access_login_allowed(const char *user) if ((0 == g_cfg->sec.ts_users_enable) && (0==g_cfg->sec.ts_always_group_check)) { - LOG_DBG("Terminal Server Users group is disabled, allowing authentication", - 1); + LOG_DBG("Terminal Server Users group is disabled, allowing authentication"); return 1; } @@ -98,7 +97,7 @@ access_login_mng_allowed(const char *user) if (0 == g_cfg->sec.ts_admins_enable) { LOG_DBG("[MNG] Terminal Server Admin group is disabled, " - "allowing authentication", 1); + "allowing authentication"); return 1; } diff --git a/sesman/scp_v1.c b/sesman/scp_v1.c index 60f82112..a9b5c514 100644 --- a/sesman/scp_v1.c +++ b/sesman/scp_v1.c @@ -60,7 +60,7 @@ scp_v1_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s) while ((!data) && ((retries == 0) || (current_try > 0))) { - LOG_DBG("data %d - retry %d - currenttry %d - expr %d", + LOG_DBG("data %ld - retry %d - currenttry %d - expr %d", data, retries, current_try, ((!data) && ((retries == 0) || (current_try > 0)))); diff --git a/sesman/scp_v1_mng.c b/sesman/scp_v1_mng.c index d44b9e04..840fa6b1 100644 --- a/sesman/scp_v1_mng.c +++ b/sesman/scp_v1_mng.c @@ -83,7 +83,7 @@ scp_v1_mng_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s) case SCP_SERVER_STATE_MNG_LISTREQ: /* list disconnected sessions */ slist = session_get_byuser(NULL, &scount, SESMAN_SESSION_STATUS_ALL); - LOG_DBG("sessions on TS: %d (slist: %x)", scount, slist); + LOG_DBG("sessions on TS: %d (slist: %p)", scount, slist); if (0 == slist) { log_message(LOG_LEVEL_INFO, "No sessions on Terminal Server"); From b2fea42acfc2ba34885e0a78a6de751f1b87289e Mon Sep 17 00:00:00 2001 From: Alexandre Quesnel <131881+aquesnel@users.noreply.github.com> Date: Sat, 1 Aug 2020 04:32:29 +0000 Subject: [PATCH 19/29] Adding an FAQ entry for configuring Travis CI and Cirrus CI for a fork --- faq-compile.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/faq-compile.txt b/faq-compile.txt index 09f57d84..7307e07a 100644 --- a/faq-compile.txt +++ b/faq-compile.txt @@ -32,3 +32,25 @@ Q. I get an error: "rail.c:31:35: fatal error: X11/extensions/Xrandr.h: No such A. You need to install the Xrandr development package. For Debian / Ubuntu this package is called libxrandr-dev. For SUSE / openSUSE this package is called libXrandr-devel. + +Q. How do I configure the same continuous integration bulids for my XRDP fork as the official XRDP repository? + +A. The XRDP project uses both Travis-CI.org and Cirrus-CI.com for continuous integration. + Both of these services are free for open source projects (both the official + repository and forks), and these services integrate with Github to build any + changes pushed to public Github repositories. + + To configure Travis CI for your XRDP fork on github: + 1. Follow Travis CI instructions for connecting your github account to Travis CI + https://docs.travis-ci.com/user/tutorial/#to-get-started-with-travis-ci-using-github + 2. In the Travis CI dashboard setting page select your XRDP fork repository for building pushed branches. + 3. Push a commit to a branch in your XRDP fork on github and Travis CI should + start building the branch because the XRDP repository already contain a .travis.yml file. + + To configure Cirrus CI for your XRDP fork on github: + 1. Follow Cirrus CI instructions for connecting your github account to Cirrus CI + https://cirrus-ci.org/guide/quick-start/ + 2. In the Github setting page for the Cirrus CI application, enable Cirrus CI + access to your XRDP fork repository. + 3. Push a commit to a branch in your XRDP fork on github and Cirrus CI should + start building the branch because the XRDP repository already contain a .cirrus.yml file. From 00dca40bf844a82f6dd867808b5f8bba7bcff456 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 3 Aug 2020 10:37:07 +0900 Subject: [PATCH 20/29] sesman.ini: Update Xorg path for CentOS 8 Closes #1646. --- sesman/sesman.ini.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 869f5bb5..bdeb86c7 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -75,13 +75,15 @@ SyslogLevel=DEBUG [Xorg] ; Specify the path of non-suid Xorg executable. It might differ depending -; on your distribution and version. The typical path is shown as follows: +; on your distribution and version. Find out the appropreate path for your +; environment. The typical path is known as follows: ; ; Fedora 26 or later : param=/usr/libexec/Xorg ; Debian 9 or later : param=/usr/lib/xorg/Xorg ; Ubuntu 16.04 or later : param=/usr/lib/xorg/Xorg ; Arch Linux : param=/usr/lib/Xorg ; CentOS 7 : param=/usr/bin/Xorg or param=Xorg +; CentOS 8 : param=/usr/libexec/Xorg ; param=Xorg ; Leave the rest paramaters as-is unless you understand what will happen. From 5cd36c511cab76ae4a7c14a792055449077e4fc1 Mon Sep 17 00:00:00 2001 From: bolkedebruin Date: Wed, 5 Aug 2020 17:12:16 +0200 Subject: [PATCH 21/29] Set max character buffer len to 512 per MS specification The MS specs determine that the character buffer lenngths for usernames, domains, passwords, alternate shells, etc can be up to 512 characters including the mandatory null terminator. --- common/xrdp_client_info.h | 12 +++++++----- common/xrdp_constants.h | 1 + libxrdp/xrdp_sec.c | 10 +++++----- neutrinordp/xrdp-neutrinordp.h | 7 ++++--- sesman/verify_user_pam.c | 11 +++++++---- xup/xup.h | 5 +++-- 6 files changed, 27 insertions(+), 19 deletions(-) diff --git a/common/xrdp_client_info.h b/common/xrdp_client_info.h index 297cdc55..fa7df6bf 100644 --- a/common/xrdp_client_info.h +++ b/common/xrdp_client_info.h @@ -18,6 +18,8 @@ * xrdp / xserver info / caps */ +#include "xrdp_constants.h" + #if !defined(XRDP_CLIENT_INFO_H) #define XRDP_CLIENT_INFO_H @@ -57,11 +59,11 @@ struct xrdp_client_info char hostname[32]; int build; int keylayout; - char username[256]; - char password[256]; - char domain[256]; - char program[256]; - char directory[256]; + char username[INFO_CLIENT_MAX_CB_LEN]; + char password[INFO_CLIENT_MAX_CB_LEN]; + char domain[INFO_CLIENT_MAX_CB_LEN]; + char program[INFO_CLIENT_MAX_CB_LEN]; + char directory[INFO_CLIENT_MAX_CB_LEN]; int rdp_compression; int rdp_autologin; int crypt_level; /* 1, 2, 3 = low, medium, high */ diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 93ca2086..46fffeca 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -38,6 +38,7 @@ ******************************************************************************/ #define INFO_CLIENT_NAME_BYTES 32 +#define INFO_CLIENT_MAX_CB_LEN 512 #define XRDP_MAX_BITMAP_CACHE_ID 3 #define XRDP_MAX_BITMAP_CACHE_IDX 2000 diff --git a/libxrdp/xrdp_sec.c b/libxrdp/xrdp_sec.c index c0e513e4..4ce0d509 100644 --- a/libxrdp/xrdp_sec.c +++ b/libxrdp/xrdp_sec.c @@ -735,7 +735,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s) } in_uint16_le(s, len_domain); - if (len_domain > 511) + if (len_domain >= INFO_CLIENT_MAX_CB_LEN) { DEBUG(("ERROR [xrdp_sec_process_logon_info()]: len_domain > 511")); return 1; @@ -757,7 +757,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s) self->rdp_layer->client_info.rdp_autologin = 0; } - if (len_user > 511) + if (len_user >= INFO_CLIENT_MAX_CB_LEN) { DEBUG(("ERROR [xrdp_sec_process_logon_info()]: len_user > 511")); return 1; @@ -769,7 +769,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s) } in_uint16_le(s, len_password); - if (len_password > 511) + if (len_password >= INFO_CLIENT_MAX_CB_LEN) { DEBUG(("ERROR [xrdp_sec_process_logon_info()]: len_password > 511")); return 1; @@ -781,7 +781,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s) } in_uint16_le(s, len_program); - if (len_program > 511) + if (len_program >= INFO_CLIENT_MAX_CB_LEN) { DEBUG(("ERROR [xrdp_sec_process_logon_info()]: len_program > 511")); return 1; @@ -793,7 +793,7 @@ xrdp_sec_process_logon_info(struct xrdp_sec *self, struct stream *s) } in_uint16_le(s, len_directory); - if (len_directory > 511) + if (len_directory >= INFO_CLIENT_MAX_CB_LEN) { DEBUG(("ERROR [xrdp_sec_process_logon_info()]: len_directory > 511")); return 1; diff --git a/neutrinordp/xrdp-neutrinordp.h b/neutrinordp/xrdp-neutrinordp.h index b68eb43a..f50a299e 100644 --- a/neutrinordp/xrdp-neutrinordp.h +++ b/neutrinordp/xrdp-neutrinordp.h @@ -24,6 +24,7 @@ #include "defines.h" #include "xrdp_rail.h" #include "xrdp_client_info.h" +#include "xrdp_constants.h" /* this is the freerdp main header */ #include @@ -196,9 +197,9 @@ struct mod int vmaj; int vmin; int vrev; - char username[256]; - char password[256]; - char domain[256]; + char username[INFO_CLIENT_MAX_CB_LEN]; + char password[INFO_CLIENT_MAX_CB_LEN]; + char domain[INFO_CLIENT_MAX_CB_LEN]; int bool_keyBoardSynced ; /* Numlock can be out of sync, we hold state here to resolve */ int keyBoardLockInfo ; /* Holds initial numlock capslock state */ diff --git a/sesman/verify_user_pam.c b/sesman/verify_user_pam.c index 05e941bb..312bcbc8 100644 --- a/sesman/verify_user_pam.c +++ b/sesman/verify_user_pam.c @@ -34,10 +34,13 @@ #include #include +/* Defines the maximum size of a username or password. With pam there is no real limit */ +#define MAX_BUF 8192 + struct t_user_pass { - char user[256]; - char pass[256]; + char user[MAX_BUF]; + char pass[MAX_BUF]; }; struct t_auth_info @@ -115,8 +118,8 @@ auth_userpass(const char *user, const char *pass, int *errorcode) get_service_name(service_name); auth_info = g_new0(struct t_auth_info, 1); - g_strncpy(auth_info->user_pass.user, user, 255); - g_strncpy(auth_info->user_pass.pass, pass, 255); + g_strncpy(auth_info->user_pass.user, user, MAX_BUF - 1); + g_strncpy(auth_info->user_pass.pass, pass, MAX_BUF - 1); auth_info->pamc.conv = &verify_pam_conv; auth_info->pamc.appdata_ptr = &(auth_info->user_pass); error = pam_start(service_name, 0, &(auth_info->pamc), &(auth_info->ph)); diff --git a/xup/xup.h b/xup/xup.h index ddd24e36..f7de3a17 100644 --- a/xup/xup.h +++ b/xup/xup.h @@ -24,6 +24,7 @@ #include "os_calls.h" #include "defines.h" #include "xrdp_client_info.h" +#include "xrdp_constants.h" #include "xrdp_rail.h" #define CURRENT_MOD_VER 4 @@ -154,8 +155,8 @@ struct mod int height; int bpp; int sck_closed; - char username[256]; - char password[256]; + char username[INFO_CLIENT_MAX_CB_LEN]; + char password[INFO_CLIENT_MAX_CB_LEN]; char ip[256]; char port[256]; int shift_state; From 47e1c5d35971a1f940afbaa7f0537728d31a8edc Mon Sep 17 00:00:00 2001 From: Bolke de Bruin Date: Wed, 19 Aug 2020 12:50:01 +0200 Subject: [PATCH 22/29] Add description --- common/xrdp_constants.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 46fffeca..50be8536 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -38,6 +38,10 @@ ******************************************************************************/ #define INFO_CLIENT_NAME_BYTES 32 +/** + * Maximum length of a string (two bytes + len), excluding the terminator + * [MS-RDPBCGR] TS_INFO_PACKET(2.2.1.11.1.1) + */ #define INFO_CLIENT_MAX_CB_LEN 512 #define XRDP_MAX_BITMAP_CACHE_ID 3 From 4d7b916fafd43867eebc9d805013ba9af3096b21 Mon Sep 17 00:00:00 2001 From: Bolke de Bruin Date: Fri, 21 Aug 2020 09:19:49 +0200 Subject: [PATCH 23/29] Improve description --- common/xrdp_constants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/xrdp_constants.h b/common/xrdp_constants.h index 50be8536..7b777e3d 100644 --- a/common/xrdp_constants.h +++ b/common/xrdp_constants.h @@ -39,7 +39,7 @@ #define INFO_CLIENT_NAME_BYTES 32 /** - * Maximum length of a string (two bytes + len), excluding the terminator + * Maximum length of a string including the mandatory null terminator * [MS-RDPBCGR] TS_INFO_PACKET(2.2.1.11.1.1) */ #define INFO_CLIENT_MAX_CB_LEN 512 From e89f124afe1798c923423f4b7fa6d67df7950d56 Mon Sep 17 00:00:00 2001 From: Bolke de Bruin Date: Fri, 21 Aug 2020 14:29:01 +0200 Subject: [PATCH 24/29] Ensure copying of the whole username/password --- xup/xup.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xup/xup.c b/xup/xup.c index e6321651..a235194b 100644 --- a/xup/xup.c +++ b/xup/xup.c @@ -1546,11 +1546,11 @@ lib_mod_set_param(struct mod *mod, const char *name, const char *value) { if (g_strcasecmp(name, "username") == 0) { - g_strncpy(mod->username, value, 255); + g_strncpy(mod->username, value, INFO_CLIENT_MAX_CB_LEN-1); } else if (g_strcasecmp(name, "password") == 0) { - g_strncpy(mod->password, value, 255); + g_strncpy(mod->password, value, INFO_CLIENT_MAX_CB_LEN-1); } else if (g_strcasecmp(name, "ip") == 0) { From 8bd7309d72ea37eb5fea649298ebdf7ae9616012 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 26 Aug 2020 10:04:33 +0100 Subject: [PATCH 25/29] Updated sesman.ini comment for Policy= in line with the manpage --- sesman/sesman.ini.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index bdeb86c7..f9cb1891 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -55,7 +55,7 @@ IdleTimeLimit=0 ;; Policy - session allocation policy ; Type: enum [ "Default" | "UBD" | "UBI" | "UBC" | "UBDI" | "UBDC" ] -; Default: Xrdp: and Xvnc: +; "Default" session per ; "UBD" session per ; "UBI" session per ; "UBC" session per From a9eb5a17d272817a45a75f5e7613f0d286d2a073 Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Sun, 14 Jun 2020 02:59:20 +0200 Subject: [PATCH 26/29] Detect and setup Programmer Dvorak keyboard layout This patch will add the keyboard layout identifier for the Programmer Dvorak keyboard layout, so that if a Windows client has that layout active, it will be mapped to the corresponding xkeyboard-config layout in the X server. An XFreeRDP client knows about this layout too, and will correspondingly map it to the identifier given here, making the layout propagate correctly through X-to-X connections as well. To replicate the full Windows keyboard layout several options have to be set as well as the main layout. To avoid having these options spill over to other layouts that are dumped, the old settings are stored before the dump and then restored afterwards. --- genkeymap/dump-keymaps.sh | 6 + instfiles/Makefile.am | 3 +- instfiles/km-19360409.ini | 1047 +++++++++++++++++++++++++++++++++++++ xrdp/xrdp_keyboard.ini | 3 + 4 files changed, 1058 insertions(+), 1 deletion(-) create mode 100644 instfiles/km-19360409.ini diff --git a/genkeymap/dump-keymaps.sh b/genkeymap/dump-keymaps.sh index 7b61f240..b95305c7 100755 --- a/genkeymap/dump-keymaps.sh +++ b/genkeymap/dump-keymaps.sh @@ -15,6 +15,12 @@ setxkbmap -model pc104 -layout us setxkbmap -model pc104 -layout dvorak ./xrdp-genkeymap ../instfiles/km-00010409.ini +# English - US 'dvp' 0x19360409 +OLD_SETTINGS=$(setxkbmap -query -verbose 4 | sed "s/^\([a-z]\+\):\s*\(.*\)$/-\1 \2/;s/^-options/-option \"\" -option/;s/,/ -option /g" | xargs -d \\n) +setxkbmap -rules xfree86 -model pc105 -layout us -variant dvp -option "" -option compose:102 -option caps:shift -option numpad:sg -option numpad:shift3 -option keypad:hex -option keypad:atm -option kpdl:semi -option lv3:ralt_alt +./xrdp-genkeymap ../instfiles/km-19360409.ini +setxkbmap ${OLD_SETTINGS} + # English - UK 'en-GB' 0x00000809 setxkbmap -model pc105 -layout gb ./xrdp-genkeymap ../instfiles/km-00000809.ini diff --git a/instfiles/Makefile.am b/instfiles/Makefile.am index 08730c03..a318ed8f 100644 --- a/instfiles/Makefile.am +++ b/instfiles/Makefile.am @@ -50,7 +50,8 @@ dist_startscript_DATA = \ km-00000813.ini \ km-00000816.ini \ km-0000100c.ini \ - km-00010409.ini + km-00010409.ini \ + km-19360409.ini # # platform specific files diff --git a/instfiles/km-19360409.ini b/instfiles/km-19360409.ini new file mode 100644 index 00000000..a8a1949c --- /dev/null +++ b/instfiles/km-19360409.ini @@ -0,0 +1,1047 @@ +[noshift] +Key8=65406:0 +Key9=65307:27 +Key10=38:38 +Key11=91:91 +Key12=123:123 +Key13=125:125 +Key14=40:40 +Key15=61:61 +Key16=42:42 +Key17=41:41 +Key18=43:43 +Key19=93:93 +Key20=33:33 +Key21=35:35 +Key22=65288:8 +Key23=65289:9 +Key24=59:59 +Key25=44:44 +Key26=46:46 +Key27=112:112 +Key28=121:121 +Key29=102:102 +Key30=103:103 +Key31=99:99 +Key32=114:114 +Key33=108:108 +Key34=47:47 +Key35=64:64 +Key36=65293:13 +Key37=65507:0 +Key38=97:97 +Key39=111:111 +Key40=101:101 +Key41=117:117 +Key42=105:105 +Key43=100:100 +Key44=104:104 +Key45=116:116 +Key46=110:110 +Key47=115:115 +Key48=45:45 +Key49=36:36 +Key50=65505:0 +Key51=92:92 +Key52=39:39 +Key53=113:113 +Key54=106:106 +Key55=107:107 +Key56=120:120 +Key57=98:98 +Key58=109:109 +Key59=119:119 +Key60=118:118 +Key61=122:122 +Key62=65506:0 +Key63=65450:42 +Key64=65513:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65514:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=0:0 +Key126=65469:61 +Key127=0:0 +Key128=0:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[shift] +Key8=65406:0 +Key9=65307:27 +Key10=37:37 +Key11=55:55 +Key12=53:53 +Key13=51:51 +Key14=49:49 +Key15=57:57 +Key16=48:48 +Key17=50:50 +Key18=52:52 +Key19=54:54 +Key20=56:56 +Key21=96:96 +Key22=65288:8 +Key23=65056:0 +Key24=58:58 +Key25=60:60 +Key26=62:62 +Key27=80:80 +Key28=89:89 +Key29=70:70 +Key30=71:71 +Key31=67:67 +Key32=82:82 +Key33=76:76 +Key34=63:63 +Key35=94:94 +Key36=65293:13 +Key37=65507:0 +Key38=65:65 +Key39=79:79 +Key40=69:69 +Key41=85:85 +Key42=73:73 +Key43=68:68 +Key44=72:72 +Key45=84:84 +Key46=78:78 +Key47=83:83 +Key48=95:95 +Key49=126:126 +Key50=65505:0 +Key51=124:124 +Key52=34:34 +Key53=81:81 +Key54=74:74 +Key55=75:75 +Key56=88:88 +Key57=66:66 +Key58=77:77 +Key59=87:87 +Key60=86:86 +Key61=90:90 +Key62=65506:0 +Key63=65450:42 +Key64=65511:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65512:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=65513:0 +Key126=61:61 +Key127=65515:0 +Key128=65517:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[altgr] +Key8=65406:0 +Key9=65307:27 +Key10=38:38 +Key11=164:164 +Key12=162:162 +Key13=165:165 +Key14=8364:8364 +Key15=163:163 +Key16=0:0 +Key17=189:189 +Key18=65104:96 +Key19=0:0 +Key20=161:161 +Key21=65104:96 +Key22=65288:8 +Key23=65289:9 +Key24=65111:168 +Key25=171:171 +Key26=187:187 +Key27=182:182 +Key28=252:252 +Key29=102:102 +Key30=103:103 +Key31=231:231 +Key32=174:174 +Key33=108:108 +Key34=191:191 +Key35=65106:94 +Key36=65293:13 +Key37=65507:0 +Key38=229:229 +Key39=248:248 +Key40=230:230 +Key41=233:233 +Key42=105:105 +Key43=240:240 +Key44=65105:180 +Key45=254:254 +Key46=241:241 +Key47=223:223 +Key48=173:173 +Key49=65107:126 +Key50=65505:0 +Key51=92:92 +Key52=65105:180 +Key53=113:113 +Key54=106:106 +Key55=107:107 +Key56=120:120 +Key57=98:98 +Key58=109:109 +Key59=119:119 +Key60=118:118 +Key61=122:122 +Key62=65506:0 +Key63=65450:42 +Key64=65513:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65514:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=0:0 +Key126=65469:61 +Key127=0:0 +Key128=0:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[shiftaltgr] +Key8=65406:0 +Key9=65307:27 +Key10=37:37 +Key11=0:0 +Key12=0:0 +Key13=0:0 +Key14=0:0 +Key15=65106:94 +Key16=0:0 +Key17=0:0 +Key18=0:0 +Key19=0:0 +Key20=16789016:11800 +Key21=0:0 +Key22=65288:8 +Key23=65056:0 +Key24=65111:168 +Key25=16785436:8220 +Key26=16785437:8221 +Key27=167:167 +Key28=220:220 +Key29=70:70 +Key30=71:71 +Key31=199:199 +Key32=2761:8482 +Key33=76:76 +Key34=16785469:8253 +Key35=65114:711 +Key36=65293:13 +Key37=65507:0 +Key38=197:197 +Key39=216:216 +Key40=198:198 +Key41=201:201 +Key42=73:73 +Key43=208:208 +Key44=0:0 +Key45=222:222 +Key46=209:209 +Key47=0:0 +Key48=2730:8211 +Key49=65107:126 +Key50=65505:0 +Key51=124:124 +Key52=65113:733 +Key53=81:81 +Key54=74:74 +Key55=75:75 +Key56=88:88 +Key57=66:66 +Key58=77:77 +Key59=87:87 +Key60=86:86 +Key61=90:90 +Key62=65506:0 +Key63=65450:42 +Key64=65511:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65512:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=65513:0 +Key126=61:61 +Key127=65515:0 +Key128=65517:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[capslock] +Key8=65406:0 +Key9=65307:27 +Key10=38:38 +Key11=55:55 +Key12=53:53 +Key13=51:51 +Key14=49:49 +Key15=57:57 +Key16=48:48 +Key17=50:50 +Key18=52:52 +Key19=54:54 +Key20=56:56 +Key21=35:35 +Key22=65288:8 +Key23=65289:9 +Key24=59:59 +Key25=44:44 +Key26=46:46 +Key27=80:80 +Key28=89:89 +Key29=70:70 +Key30=71:71 +Key31=67:67 +Key32=82:82 +Key33=76:76 +Key34=47:47 +Key35=64:64 +Key36=65293:13 +Key37=65507:0 +Key38=65:65 +Key39=79:79 +Key40=69:69 +Key41=85:85 +Key42=73:73 +Key43=68:68 +Key44=72:72 +Key45=84:84 +Key46=78:78 +Key47=83:83 +Key48=95:95 +Key49=36:36 +Key50=65505:0 +Key51=92:92 +Key52=39:39 +Key53=81:81 +Key54=74:74 +Key55=75:75 +Key56=88:88 +Key57=66:66 +Key58=77:77 +Key59=87:87 +Key60=86:86 +Key61=90:90 +Key62=65506:0 +Key63=65450:42 +Key64=65513:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65514:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=0:0 +Key126=65469:61 +Key127=0:0 +Key128=0:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[capslockaltgr] +Key8=65406:0 +Key9=65307:27 +Key10=38:38 +Key11=0:0 +Key12=0:0 +Key13=0:0 +Key14=0:0 +Key15=65106:94 +Key16=0:0 +Key17=0:0 +Key18=0:0 +Key19=0:0 +Key20=16789016:11800 +Key21=65104:96 +Key22=65288:8 +Key23=65289:9 +Key24=65111:168 +Key25=171:171 +Key26=187:187 +Key27=182:182 +Key28=220:220 +Key29=70:70 +Key30=71:71 +Key31=199:199 +Key32=174:174 +Key33=76:76 +Key34=191:191 +Key35=65106:94 +Key36=65293:13 +Key37=65507:0 +Key38=197:197 +Key39=216:216 +Key40=198:198 +Key41=201:201 +Key42=73:73 +Key43=208:208 +Key44=65105:180 +Key45=222:222 +Key46=209:209 +Key47=223:223 +Key48=2730:8211 +Key49=65107:126 +Key50=65505:0 +Key51=92:92 +Key52=65105:180 +Key53=81:81 +Key54=74:74 +Key55=75:75 +Key56=88:88 +Key57=66:66 +Key58=77:77 +Key59=87:87 +Key60=86:86 +Key61=90:90 +Key62=65506:0 +Key63=65450:42 +Key64=65513:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65514:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=0:0 +Key126=65469:61 +Key127=0:0 +Key128=0:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[shiftcapslock] +Key8=65406:0 +Key9=65307:27 +Key10=37:37 +Key11=91:91 +Key12=123:123 +Key13=125:125 +Key14=40:40 +Key15=61:61 +Key16=42:42 +Key17=41:41 +Key18=43:43 +Key19=93:93 +Key20=33:33 +Key21=96:96 +Key22=65288:8 +Key23=65056:0 +Key24=58:58 +Key25=60:60 +Key26=62:62 +Key27=112:112 +Key28=121:121 +Key29=102:102 +Key30=103:103 +Key31=99:99 +Key32=114:114 +Key33=108:108 +Key34=63:63 +Key35=94:94 +Key36=65293:13 +Key37=65507:0 +Key38=97:97 +Key39=111:111 +Key40=101:101 +Key41=117:117 +Key42=105:105 +Key43=100:100 +Key44=104:104 +Key45=116:116 +Key46=110:110 +Key47=115:115 +Key48=45:45 +Key49=126:126 +Key50=65505:0 +Key51=124:124 +Key52=34:34 +Key53=113:113 +Key54=106:106 +Key55=107:107 +Key56=120:120 +Key57=98:98 +Key58=109:109 +Key59=119:119 +Key60=118:118 +Key61=122:122 +Key62=65506:0 +Key63=65450:42 +Key64=65511:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65512:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=65513:0 +Key126=61:61 +Key127=65515:0 +Key128=65517:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 + +[shiftcapslockaltgr] +Key8=65406:0 +Key9=65307:27 +Key10=37:37 +Key11=164:164 +Key12=162:162 +Key13=165:165 +Key14=8364:8364 +Key15=163:163 +Key16=0:0 +Key17=189:189 +Key18=65104:96 +Key19=0:0 +Key20=161:161 +Key21=0:0 +Key22=65288:8 +Key23=65056:0 +Key24=65111:168 +Key25=16785436:8220 +Key26=16785437:8221 +Key27=167:167 +Key28=252:252 +Key29=102:102 +Key30=103:103 +Key31=231:231 +Key32=2761:8482 +Key33=108:108 +Key34=16785469:8253 +Key35=65114:711 +Key36=65293:13 +Key37=65507:0 +Key38=229:229 +Key39=248:248 +Key40=230:230 +Key41=233:233 +Key42=105:105 +Key43=240:240 +Key44=0:0 +Key45=254:254 +Key46=241:241 +Key47=0:0 +Key48=173:173 +Key49=65107:126 +Key50=65505:0 +Key51=124:124 +Key52=65113:733 +Key53=113:113 +Key54=106:106 +Key55=107:107 +Key56=120:120 +Key57=98:98 +Key58=109:109 +Key59=119:119 +Key60=118:118 +Key61=122:122 +Key62=65506:0 +Key63=65450:42 +Key64=65511:0 +Key65=32:32 +Key66=65509:0 +Key67=65470:0 +Key68=65471:0 +Key69=65472:0 +Key70=65473:0 +Key71=65474:0 +Key72=65475:0 +Key73=65476:0 +Key74=65477:0 +Key75=65478:0 +Key76=65479:0 +Key77=65407:0 +Key78=65300:0 +Key79=65429:0 +Key80=65431:0 +Key81=65434:0 +Key82=65453:45 +Key83=65430:0 +Key84=65437:0 +Key85=65432:0 +Key86=65451:43 +Key87=65436:0 +Key88=65433:0 +Key89=65435:0 +Key90=65438:0 +Key91=65439:0 +Key92=0:0 +Key93=0:0 +Key94=65312:0 +Key95=65480:0 +Key96=65481:0 +Key97=65360:0 +Key98=65362:0 +Key99=65365:0 +Key100=65361:0 +Key101=0:0 +Key102=65363:0 +Key103=65367:0 +Key104=65364:0 +Key105=65366:0 +Key106=65379:0 +Key107=65535:127 +Key108=65421:13 +Key109=65508:0 +Key110=65299:0 +Key111=65377:0 +Key112=65455:47 +Key113=65512:0 +Key114=0:0 +Key115=65515:0 +Key116=65516:0 +Key117=65383:0 +Key118=0:0 +Key119=0:0 +Key120=0:0 +Key121=0:0 +Key122=0:0 +Key123=0:0 +Key124=65027:0 +Key125=65513:0 +Key126=61:61 +Key127=65515:0 +Key128=65517:0 +Key129=0:0 +Key130=0:0 +Key131=0:0 +Key132=0:0 +Key133=0:0 +Key134=65454:46 +Key135=0:0 +Key136=0:0 diff --git a/xrdp/xrdp_keyboard.ini b/xrdp/xrdp_keyboard.ini index e0d7c6da..44b4bce4 100644 --- a/xrdp/xrdp_keyboard.ini +++ b/xrdp/xrdp_keyboard.ini @@ -59,6 +59,7 @@ layouts_map=default_layouts_map [default_rdp_layouts] rdp_layout_us=0x00000409 rdp_layout_us_dvorak=0x00010409 +rdp_layout_us_dvp=0x19360409 rdp_layout_dk=0x00000406 rdp_layout_de=0x00000407 rdp_layout_es=0x0000040A @@ -85,6 +86,7 @@ rdp_layout_pt=0x00000816 [default_layouts_map] rdp_layout_us=us rdp_layout_us_dvorak=dvorak +rdp_layout_us_dvp=us(dvp) rdp_layout_dk=dk rdp_layout_de=de rdp_layout_es=es @@ -122,6 +124,7 @@ layouts_map=default_layouts_map [rdp_layouts_map_mac] rdp_layout_us=us rdp_layout_us_dvorak=dvorak +rdp_layout_us_dvp=us(dvp) rdp_layout_dk=dk rdp_layout_de=de rdp_layout_es=es From d364e76ae8137b26578b675e29bb74f4215452b8 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Tue, 30 Jun 2020 15:00:24 +0900 Subject: [PATCH 27/29] Update NEWS for v0.9.13.1 --- NEWS.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 6d949ba7..3f137919 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,17 @@ +# Release notes for xrdp v0.9.13.1 (2020/06/30) + +This is a security fix release that includes fixes for the following local buffer overflow vulnerability. + +* [CVE-2022-4044: Local users can perform a buffer overflow attack against the xrdp-sesman service and then impersonate it](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-4044) + +This update is recommended for all xrdp users. + +## Special thanks + +Thanks to [Ashley Newson](https://github.com/ashleynewson) reporting the vulnerability and reviewing fix. + +----------------------- + # Release notes for xrdp v0.9.13 (2020/03/11) This release is an intermediate bugfix release. The previous version v0.9.12 has some regressions on drive redirection. @@ -111,7 +125,7 @@ Thank you for matt335672 contributing to lots of improvements in drive redirecti ----------------------- -## Release notes for xrdp v0.9.9 (2018/12/25) +# Release notes for xrdp v0.9.9 (2018/12/25) ## Release cycle From the next release, release cycle will be changed from quarterly to every From b4ca302c0512929c40f23ede1550f1ddcdd62eac Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 31 Aug 2020 14:15:12 +0900 Subject: [PATCH 28/29] Update NEWS for v0.9.14 --- NEWS.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/NEWS.md b/NEWS.md index 3f137919..e57c13fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,34 @@ +# Release notes for xrdp v0.9.14 (2020/08/31) + +## New features +* VNC multi-monitor support if you are using a suitable Xvnc server #1343 +* VNC sessions now resize by default on reconnection if you are using a suitable Xvnc server #1343 +* Support Slackware for PAM #1558 #1560 +* Support Programmer Dvorak Keyboard #1663 + +**[HEADS UP]** The VNC changes are significant. They described in more detail on the following wiki page. +* [Xvnc backend : Multi monitor and resize support](https://github.com/neutrinolabs/xrdp/wiki/Xvnc-backend-:-Multi-monitor-and-resize-support) + +## Bug fixes +* Fix odd shift key behavior (workaround) #397 #1522 +* Fix Xorg path in the document for Arch Linux #1448 #1529 +* Fix Xorg path in the document for CentOS 8 #1646 #1647 +* Fix internal username/password buffer is smaller than RDP protocol specification #1648 #1653 +* Fix possible memory out-of-bounds accesses #1549 +* Fix memory allocation overflow #1557 +* Prevent chansrv input channels being scanned during a server reset #1595 +* Ignore TS_MULTIFRAGMENTUPDATE_CAPABILITYSET from client if fp disabled #1593 +* Minor manpage fixes #1611 + +## Other changes +* CI error fixes +* Introduce cppcheck + +## Known issues +* FreeRDP 2.0.0-rc4 or later might not able to connect to xrdp due to + xrdp's bad-mannered behaviour, add `+glyph-cache` option to FreeRDP to connect #1266 +* Audio redirection by MP3 codec doesn't sound with some client, use AAC instead #965 + # Release notes for xrdp v0.9.13.1 (2020/06/30) This is a security fix release that includes fixes for the following local buffer overflow vulnerability. From 9ec6162a54d54a60434b5bdc093aa1c9f876f204 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 31 Aug 2020 14:24:41 +0900 Subject: [PATCH 29/29] bump version to v0.9.14 --- README.md | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 84ba4615..21f755b5 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp-questions) ![Apache-License](https://img.shields.io/badge/License-Apache%202.0-blue.svg) -*Current Version:* 0.9.13 +*Current Version:* 0.9.14 # xrdp - an open source RDP server diff --git a/configure.ac b/configure.ac index d5fc41bc..8a2d2301 100644 --- a/configure.ac +++ b/configure.ac @@ -1,7 +1,7 @@ # Process this file with autoconf to produce a configure script AC_PREREQ(2.65) -AC_INIT([xrdp], [0.9.13], [xrdp-devel@googlegroups.com]) +AC_INIT([xrdp], [0.9.14], [xrdp-devel@googlegroups.com]) AC_CONFIG_HEADERS(config_ac.h:config_ac-h.in) AM_INIT_AUTOMAKE([1.7.2 foreign]) AC_CONFIG_MACRO_DIR([m4])