From 3de1e966b4c4113fd901f9ecd00cf59a3759e745 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 12 Jan 2022 11:41:26 +0000 Subject: Support imlib2 on RHEL 7 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 44ac95a6..a7a10bf6 100644 --- a/configure.ac +++ b/configure.ac @@ -224,7 +224,7 @@ case "$with_imlib2" in use_imlib2=no ;; yes) - PKG_CHECK_MODULES([IMLIB2], [imlib2 >= 1.4.10], + PKG_CHECK_MODULES([IMLIB2], [imlib2 >= 1.4.5], [use_imlib2=yes], [AC_MSG_ERROR([please install libimlib2-dev or imlib2-devel])]) ;; -- cgit v1.2.3 From 69ea4064406699da105bcf0936586b10bba0657b Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 14:04:09 +0900 Subject: Add g_str_to_bitmask utility function It should be used for comma separated configuration to bitmask. e.g. RestrictOutboundClipboard = text, file, image --- common/string_calls.c | 69 ++++++++ common/string_calls.h | 14 ++ tests/common/test_string_calls.c | 345 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+) diff --git a/common/string_calls.c b/common/string_calls.c index 9950fd09..18f54512 100644 --- a/common/string_calls.c +++ b/common/string_calls.c @@ -986,3 +986,72 @@ g_bitmask_to_str(int bitmask, const struct bitmask_string bitdefs[], return rlen; } + +int +g_str_to_bitmask(const char *str, const struct bitmask_string bitdefs[], + const char *delim, char *unrecognised, int unrecognised_len) +{ + char *properties = NULL; + char *p = NULL; + int mask = 0; + + if (unrecognised_len < 1) + { + /* No space left to tell unrecognised tokens */ + return 0; + } + if (!unrecognised) + { + return 0; + } + /* ensure not to return with uninitialized buffer */ + unrecognised[0] = '\0'; + if (!str || !bitdefs || !delim) + { + return 0; + } + properties = g_strdup(str); + if (!properties) + { + return 0; + } + p = strtok(properties, delim); + while (p != NULL) + { + g_strtrim(p, 3); + const struct bitmask_string *b; + int found = 0; + for (b = &bitdefs[0] ; b->str != NULL; ++b) + { + if (0 == g_strcasecmp(p, b->str)) + { + mask |= b->mask; + found = 1; + break; + } + } + if (found == 0) + { + int length = g_strlen(unrecognised); + if (length > 0) + { + /* adding ",property" */ + if (length + g_strlen(p) + 1 < unrecognised_len) + { + unrecognised[length] = delim[0]; + length += 1; + g_strcpy(unrecognised + length, p); + } + } + else if (g_strlen(p) < unrecognised_len) + { + g_strcpy(unrecognised, p); + } + } + p = strtok(NULL, delim); + } + + g_free(properties); + return mask; +} + diff --git a/common/string_calls.h b/common/string_calls.h index 990a98e6..87888c18 100644 --- a/common/string_calls.h +++ b/common/string_calls.h @@ -174,6 +174,20 @@ int g_bitmask_to_str(int bitmask, const struct bitmask_string[], char delim, char *buff, int bufflen); +/*** + * Converts a string containing a series of tokens to a bitmask. + * @param str Input string + * @param bitmask_string Array mapping tokens to bitmask values + * @param delim Delimiter for tokens in str + * @param[out] unrecognised Buffer for any unrecognised tokens + * @param unrecognised_len Length of unrecognised including '\0'; + * @return bitmask value for recognised tokens + */ +int +g_str_to_bitmask(const char *str, const struct bitmask_string[], + const char *delim, char *unrecognised, + int unrecognised_len); + int g_strlen(const char *text); char *g_strchr(const char *text, int c); char *g_strrchr(const char *text, int c); diff --git a/tests/common/test_string_calls.c b/tests/common/test_string_calls.c index cd15c72f..58dcaea0 100644 --- a/tests/common/test_string_calls.c +++ b/tests/common/test_string_calls.c @@ -323,6 +323,331 @@ START_TEST(test_bm2str__overflow_some_bits_undefined) } END_TEST +START_TEST(test_str2bm__null_string) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask(NULL, bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__empty_string) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask("", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__null_bitdefs) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + rv = g_str_to_bitmask("BIT_0", NULL, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__null_delim) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask("BIT_0", bits, NULL, buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__null_buffer) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask("BIT_0", bits, ",", NULL, sizeof(buff)); + + ck_assert_str_eq(buff, "dummy"); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__zero_buffer) +{ + int rv; + char buff[1]; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask("BIT_0", bits, ",", buff, 0); + + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__zero_mask) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {0, "ZERO MASK"}, /* mask 0 should not be detected as end of list */ + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0; + rv = g_str_to_bitmask("BIT_0", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__all_defined) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0 | 1 << 1; + rv = g_str_to_bitmask("BIT_0,BIT_1", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__no_defined) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 0; + rv = g_str_to_bitmask("BIT_2,BIT_3", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, "BIT_2,BIT_3"); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__some_defined) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + {1 << 2, "BIT_2"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 1; + rv = g_str_to_bitmask("a,BIT_1,b", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, "a,b"); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__trim_space) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + {1 << 2, "BIT_2"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0 | 1 << 1 | 1 << 2; + rv = g_str_to_bitmask("BIT_0 , BIT_1 , BIT_2", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__overflow_undefined) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + {1 << 2, "BIT_2"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 1; + rv = g_str_to_bitmask("123456789,BIT_1,abcdef", bits, ",", buff, sizeof(buff)); + + /* abcdef is not filled */ + ck_assert_str_eq(buff, "123456789"); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__delim_slash) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + {1 << 2, "BIT_2"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0 | 1 << 1 | 1 << 2; + rv = g_str_to_bitmask("BIT_0/BIT_1/BIT_2", bits, "/", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__no_delim) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + BITMASK_STRING_END_OF_LIST + }; + + rv = g_str_to_bitmask("BIT_0,BIT_1", bits, "", buff, sizeof(buff)); + + ck_assert_str_eq(buff, "BIT_0,BIT_1"); + ck_assert_int_eq(rv, 0); +} +END_TEST + +START_TEST(test_str2bm__multiple_delim) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + {1 << 2, "BIT_2"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0 | 1 << 1 | 1 << 2; + rv = g_str_to_bitmask("BIT_0/BIT_1,BIT_2", bits, ",/", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__first_delim_is_semicolon) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + {1 << 1, "BIT_1"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 1; + rv = g_str_to_bitmask("a;b;BIT_1;c", bits, ";,", buff, sizeof(buff)); + + ck_assert_str_eq(buff, "a;b;c"); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + +START_TEST(test_str2bm__empty_token) +{ + int rv; + char buff[16] = { 'd', 'u', 'm', 'm', 'y' }; + + static const struct bitmask_string bits[] = + { + {1 << 0, "BIT_0"}, + BITMASK_STRING_END_OF_LIST + }; + + int bitmask = 1 << 0; + rv = g_str_to_bitmask(",BIT_0, ,", bits, ",", buff, sizeof(buff)); + + ck_assert_str_eq(buff, ""); + ck_assert_int_eq(rv, bitmask); +} +END_TEST + /******************************************************************************/ START_TEST(test_strtrim__trim_left) @@ -385,6 +710,7 @@ make_suite_test_string(void) Suite *s; TCase *tc_strnjoin; TCase *tc_bm2str; + TCase *tc_str2bm; TCase *tc_strtrim; s = suite_create("String"); @@ -410,6 +736,25 @@ make_suite_test_string(void) tcase_add_test(tc_bm2str, test_bm2str__some_bits_undefined); tcase_add_test(tc_bm2str, test_bm2str__overflow_all_bits_defined); tcase_add_test(tc_bm2str, test_bm2str__overflow_some_bits_undefined); + tc_str2bm = tcase_create("str2bm"); + suite_add_tcase(s, tc_str2bm); + tcase_add_test(tc_str2bm, test_str2bm__null_string); + tcase_add_test(tc_str2bm, test_str2bm__empty_string); + tcase_add_test(tc_str2bm, test_str2bm__null_bitdefs); + tcase_add_test(tc_str2bm, test_str2bm__null_delim); + tcase_add_test(tc_str2bm, test_str2bm__null_buffer); + tcase_add_test(tc_str2bm, test_str2bm__zero_buffer); + tcase_add_test(tc_str2bm, test_str2bm__zero_mask); + tcase_add_test(tc_str2bm, test_str2bm__all_defined); + tcase_add_test(tc_str2bm, test_str2bm__no_defined); + tcase_add_test(tc_str2bm, test_str2bm__some_defined); + tcase_add_test(tc_str2bm, test_str2bm__trim_space); + tcase_add_test(tc_str2bm, test_str2bm__overflow_undefined); + tcase_add_test(tc_str2bm, test_str2bm__no_delim); + tcase_add_test(tc_str2bm, test_str2bm__delim_slash); + tcase_add_test(tc_str2bm, test_str2bm__multiple_delim); + tcase_add_test(tc_str2bm, test_str2bm__first_delim_is_semicolon); + tcase_add_test(tc_str2bm, test_str2bm__empty_token); tc_strtrim = tcase_create("strtrim"); suite_add_tcase(s, tc_strtrim); -- cgit v1.2.3 From bd820845057f41f135ac4406ab87732db9c868e0 Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 14:35:50 +0900 Subject: Extend In/Outbound text,file,image restriction respectively It supports the extended configurations for sesman.ini: Before: [Security] RestrictOutboundClipboard=true or false After: [Security] RestrictInboundClipboard=[true or false | text or file or image | comma separated list] RestrictOutboundClipboard=[true or false | text or file or image | comma separated list] Above configuration is disabled by default (false) And it can be specified comma separated list like this:. RestrictInboundClipboard=file, image RestrictOutboundClipboard=text, file, image Note that if RestrictOutboundClipboard=true,file is set, file is ignored and it is treated as RestrictOutboundClipboard=true It is same for RestrictInboundClipboard. --- sesman/chansrv/chansrv_common.h | 7 ++++ sesman/chansrv/chansrv_config.c | 74 ++++++++++++++++++++++++++++++++-- sesman/chansrv/chansrv_config.h | 2 + sesman/config.c | 89 +++++++++++++++++++++++++++++++++++++---- sesman/config.h | 7 ++++ 5 files changed, 168 insertions(+), 11 deletions(-) diff --git a/sesman/chansrv/chansrv_common.h b/sesman/chansrv/chansrv_common.h index d15c8da7..656e608f 100644 --- a/sesman/chansrv/chansrv_common.h +++ b/sesman/chansrv/chansrv_common.h @@ -22,6 +22,13 @@ #include "parse.h" #include "os_calls.h" +/* Define bitmask values for restricting the clipboard */ +#define CLIP_RESTRICT_NONE 0 +#define CLIP_RESTRICT_TEXT (1<<0) +#define CLIP_RESTRICT_FILE (1<<1) +#define CLIP_RESTRICT_IMAGE (1<<2) +#define CLIP_RESTRICT_ALL 0x7fffffff + int read_entire_packet(struct stream *src, struct stream **dest, int chan_flags, int length, int total_length); #endif diff --git a/sesman/chansrv/chansrv_config.c b/sesman/chansrv/chansrv_config.c index adbecb5e..64297bcd 100644 --- a/sesman/chansrv/chansrv_config.c +++ b/sesman/chansrv/chansrv_config.c @@ -30,12 +30,14 @@ #include "file.h" #include "os_calls.h" +#include "chansrv_common.h" #include "chansrv_config.h" #include "string_calls.h" /* Default settings */ #define DEFAULT_USE_UNIX_SOCKET 0 #define DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD 0 +#define DEFAULT_RESTRICT_INBOUND_CLIPBOARD 0 #define DEFAULT_ENABLE_FUSE_MOUNT 1 #define DEFAULT_FUSE_MOUNT_NAME "xrdp-client" #define DEFAULT_FILE_UMASK 077 @@ -48,6 +50,21 @@ printflike(2, 3) enum logReturns (*log_func_t)(const enum logLevels lvl, const char *msg, ...); +/* Map clipboard strings into bitmask values */ +static const struct bitmask_string clip_restrict_map[] = +{ + { CLIP_RESTRICT_TEXT, "text"}, + { CLIP_RESTRICT_FILE, "file"}, + { CLIP_RESTRICT_IMAGE, "image"}, + { CLIP_RESTRICT_ALL, "all"}, + { CLIP_RESTRICT_NONE, "none"}, + /* Compatibility values */ + { CLIP_RESTRICT_ALL, "true"}, + { CLIP_RESTRICT_ALL, "yes"}, + { CLIP_RESTRICT_NONE, "false"}, + BITMASK_STRING_END_OF_LIST +}; + /***************************************************************************//** * @brief Error logging function to use to log to stdout * @@ -125,9 +142,30 @@ read_config_security(log_func_t logmsg, const char *name = (const char *)list_get_item(names, index); const char *value = (const char *)list_get_item(values, index); + char unrecognised[256]; if (g_strcasecmp(name, "RestrictOutboundClipboard") == 0) { - cfg->restrict_outbound_clipboard = g_text2bool(value); + cfg->restrict_outbound_clipboard = + g_str_to_bitmask(value, clip_restrict_map, ",", + unrecognised, sizeof(unrecognised)); + if (unrecognised[0] != '\0') + { + LOG(LOG_LEVEL_WARNING, + "Unrecognised tokens parsing 'RestrictOutboundClipboard' %s", + unrecognised); + } + } + if (g_strcasecmp(name, "RestrictInboundClipboard") == 0) + { + cfg->restrict_inbound_clipboard = + g_str_to_bitmask(value, clip_restrict_map, ",", + unrecognised, sizeof(unrecognised)); + if (unrecognised[0] != '\0') + { + LOG(LOG_LEVEL_WARNING, + "Unrecognised tokens parsing 'RestrictInboundClipboard' %s", + unrecognised); + } } } @@ -208,6 +246,7 @@ new_config(void) cfg->use_unix_socket = DEFAULT_USE_UNIX_SOCKET; cfg->enable_fuse_mount = DEFAULT_ENABLE_FUSE_MOUNT; cfg->restrict_outbound_clipboard = DEFAULT_RESTRICT_OUTBOUND_CLIPBOARD; + cfg->restrict_inbound_clipboard = DEFAULT_RESTRICT_INBOUND_CLIPBOARD; cfg->fuse_mount_name = fuse_mount_name; cfg->file_umask = DEFAULT_FILE_UMASK; cfg->use_nautilus3_flist_format = DEFAULT_USE_NAUTILUS3_FLIST_FORMAT; @@ -286,10 +325,37 @@ config_dump(struct config_chansrv *config) g_writeln(" UseUnixSocket (derived): %s", g_bool2text(config->use_unix_socket)); + char buf[256]; g_writeln("\nSecurity configuration:"); - g_writeln(" RestrictOutboundClipboard: %s", - g_bool2text(config->restrict_outbound_clipboard)); - + if (config->restrict_outbound_clipboard == CLIP_RESTRICT_NONE) + { + g_writeln(" RestrictOutboundClipboard: %s", "none"); + } + else if (config->restrict_outbound_clipboard == CLIP_RESTRICT_ALL) + { + g_writeln(" RestrictOutboundClipboard: %s", "all"); + } + else + { + g_bitmask_to_str(config->restrict_outbound_clipboard, + clip_restrict_map, ',', buf, sizeof(buf)); + g_writeln(" RestrictOutboundClipboard: %s", buf); + } + g_writeln(" RestrictInboundClipboard: %s", buf); + if (config->restrict_inbound_clipboard == CLIP_RESTRICT_NONE) + { + g_writeln(" RestrictInboundClipboard: %s", "none"); + } + else if (config->restrict_inbound_clipboard == CLIP_RESTRICT_ALL) + { + g_writeln(" RestrictInboundClipboard: %s", "all"); + } + else + { + g_bitmask_to_str(config->restrict_inbound_clipboard, + clip_restrict_map, ',', buf, sizeof(buf)); + g_writeln(" RestrictInboundClipboard: %s", buf); + } g_writeln("\nChansrv configuration:"); g_writeln(" EnableFuseMount %s", g_bool2text(config->enable_fuse_mount)); diff --git a/sesman/chansrv/chansrv_config.h b/sesman/chansrv/chansrv_config.h index f4acd592..b0327fea 100644 --- a/sesman/chansrv/chansrv_config.h +++ b/sesman/chansrv/chansrv_config.h @@ -31,6 +31,8 @@ struct config_chansrv /** RestrictOutboundClipboard setting from sesman.ini */ int restrict_outbound_clipboard; + /** RestrictInboundClipboard setting from sesman.ini */ + int restrict_inbound_clipboard; /** * FuseMountName from sesman.ini */ char *fuse_mount_name; diff --git a/sesman/config.c b/sesman/config.c index 481ae760..61e9e403 100644 --- a/sesman/config.c +++ b/sesman/config.c @@ -34,6 +34,7 @@ #include "sesman.h" #include "log.h" #include "string_calls.h" +#include "chansrv/chansrv_common.h" /***************************************************************************//** * @@ -162,6 +163,26 @@ config_read_globals(int file, struct config_sesman *cf, struct list *param_n, return 0; } +/* + Map clipboard strings into bitmask values. + Duplicated definition exists in chansrv_config, + because it avoids build failure for xrdp-sesman and xrdp-sesrun. + It should be unified in the future. +*/ +static const struct bitmask_string clip_restrict_map[] = +{ + { CLIP_RESTRICT_TEXT, "text"}, + { CLIP_RESTRICT_FILE, "file"}, + { CLIP_RESTRICT_IMAGE, "image"}, + { CLIP_RESTRICT_ALL, "all"}, + { CLIP_RESTRICT_NONE, "none"}, + /* Compatibility values */ + { CLIP_RESTRICT_ALL, "true"}, + { CLIP_RESTRICT_ALL, "yes"}, + { CLIP_RESTRICT_NONE, "false"}, + BITMASK_STRING_END_OF_LIST +}; + /***************************************************************************//** * * @brief Reads sesman [Security] configuration section @@ -190,6 +211,7 @@ config_read_security(int file, struct config_security *sc, sc->ts_users_enable = 0; sc->ts_admins_enable = 0; sc->restrict_outbound_clipboard = 0; + sc->restrict_inbound_clipboard = 0; file_read_section(file, SESMAN_CFG_SECURITY, param_n, param_v); @@ -231,7 +253,31 @@ config_read_security(int file, struct config_security *sc, if (0 == g_strcasecmp(buf, SESMAN_CFG_SEC_RESTRICT_OUTBOUND_CLIPBOARD)) { - sc->restrict_outbound_clipboard = g_text2bool((char *)list_get_item(param_v, i)); + char unrecognised[256]; + sc->restrict_outbound_clipboard = + g_str_to_bitmask((const char *)list_get_item(param_v, i), + clip_restrict_map, ",", + unrecognised, sizeof(unrecognised)); + if (unrecognised[0] != '\0') + { + LOG(LOG_LEVEL_WARNING, + "Unrecognised tokens parsing 'RestrictOutboundClipboard' %s", + unrecognised); + } + } + if (0 == g_strcasecmp(buf, SESMAN_CFG_SEC_RESTRICT_INBOUND_CLIPBOARD)) + { + char unrecognised[256]; + sc->restrict_inbound_clipboard = + g_str_to_bitmask((const char *)list_get_item(param_v, i), + clip_restrict_map, ",", + unrecognised, sizeof(unrecognised)); + if (unrecognised[0] != '\0') + { + LOG(LOG_LEVEL_WARNING, + "Unrecognised tokens parsing 'RestrictInboundClipboard' %s", + unrecognised); + } } } @@ -553,12 +599,41 @@ config_dump(struct config_sesman *config) /* Security configuration */ g_writeln("Security configuration:"); - g_writeln(" AllowRootLogin: %d", sc->allow_root); - g_writeln(" MaxLoginRetry: %d", sc->login_retry); - g_writeln(" AlwaysGroupCheck: %d", sc->ts_always_group_check); - g_writeln(" RestrictOutboundClipboard: %d", sc->restrict_outbound_clipboard); + g_writeln(" AllowRootLogin: %d", sc->allow_root); + g_writeln(" MaxLoginRetry: %d", sc->login_retry); + g_writeln(" AlwaysGroupCheck: %d", sc->ts_always_group_check); + if (sc->restrict_outbound_clipboard == CLIP_RESTRICT_NONE) + { + g_writeln(" RestrictOutboundClipboard: %s", "none"); + } + else if (sc->restrict_outbound_clipboard == CLIP_RESTRICT_ALL) + { + g_writeln(" RestrictOutboundClipboard: %s", "all"); + } + else + { + char buf[256]; + g_bitmask_to_str(sc->restrict_outbound_clipboard, + clip_restrict_map, ',', buf, sizeof(buf)); + g_writeln(" RestrictOutboundClipboard: %s", buf); + } + if (sc->restrict_inbound_clipboard == CLIP_RESTRICT_NONE) + { + g_writeln(" RestrictInboundClipboard: %s", "none"); + } + else if (sc->restrict_inbound_clipboard == CLIP_RESTRICT_ALL) + { + g_writeln(" RestrictInboundClipboard: %s", "all"); + } + else + { + char buf[256]; + g_bitmask_to_str(sc->restrict_inbound_clipboard, + clip_restrict_map, ',', buf, sizeof(buf)); + g_writeln(" RestrictInboundClipboard: %s", buf); + } - g_printf( " TSUsersGroup: "); + g_printf( " TSUsersGroup: "); if (sc->ts_users_enable) { g_printf("%d", sc->ts_users); @@ -569,7 +644,7 @@ config_dump(struct config_sesman *config) } g_writeln("%s", ""); - g_printf( " TSAdminsGroup: "); + g_printf( " TSAdminsGroup: "); if (sc->ts_admins_enable) { g_printf("%d", sc->ts_admins); diff --git a/sesman/config.h b/sesman/config.h index d9dbcefe..c6b72f8a 100644 --- a/sesman/config.h +++ b/sesman/config.h @@ -61,6 +61,7 @@ #define SESMAN_CFG_SEC_ADM_GROUP "TerminalServerAdmins" #define SESMAN_CFG_SEC_ALWAYSGROUPCHECK "AlwaysGroupCheck" #define SESMAN_CFG_SEC_RESTRICT_OUTBOUND_CLIPBOARD "RestrictOutboundClipboard" +#define SESMAN_CFG_SEC_RESTRICT_INBOUND_CLIPBOARD "RestrictInboundClipboard" #define SESMAN_CFG_SESSIONS "Sessions" #define SESMAN_CFG_SESS_MAX "MaxSessions" @@ -134,6 +135,12 @@ struct config_security * @brief if the clipboard should be enforced restricted. If true only allow client -> server, not vice versa. */ int restrict_outbound_clipboard; + + /** + * @var restrict_inbound_clipboard + * @brief if the clipboard should be enforced restricted. If true only allow server -> client, not vice versa. + */ + int restrict_inbound_clipboard; }; /** -- cgit v1.2.3 From fb1c4ec9450e4576b97938e22858a6864da6bf9e Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 14:43:49 +0900 Subject: Block outbound clipboard text/image/file respectively RestrictOutboundClipboard kills all of test/file/image transfer via clipboard. For controlling each content type behavior, clipboard_xevent is not appropriate place to block respectively. Instead, in clipboard_event_selection_notify, these media type will be blocked which depends on the following configurations in sesman.ini [Security] section. * RestrictOutboundClipboard=text * RestrictOutboundClipboard=file * RestrictOutboundClipboard=image You can also set comma separated list * RestrictOutboundClipboard=text, file, image --- sesman/chansrv/clipboard.c | 187 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 143 insertions(+), 44 deletions(-) diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index 245a51ee..5cbaf6f5 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -171,6 +171,7 @@ x-special/gnome-copied-files #include "os_calls.h" #include "string_calls.h" #include "chansrv.h" +#include "chansrv_common.h" #include "chansrv_config.h" #include "clipboard.h" #include "clipboard_file.h" @@ -2005,14 +2006,33 @@ clipboard_event_selection_notify(XEvent *xevent) g_clip_s2c.data[g_clip_s2c.total_bytes] = 0; if (g_clip_s2c.xrdp_clip_type == XRDP_CB_FILE) { - clipboard_send_data_response_for_file(g_clip_s2c.data, - g_clip_s2c.total_bytes); + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(file) UTF8_STRING(%s) is restricted because of config", + g_clip_s2c.data); + } + else + { + clipboard_send_data_response_for_file(g_clip_s2c.data, + g_clip_s2c.total_bytes); + } } else { - clipboard_send_data_response_for_text(g_clip_s2c.data, - g_clip_s2c.total_bytes); + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(text) UTF8_STRING(%s) is restricted because of config", + g_clip_s2c.data); + } + else + { + clipboard_send_data_response_for_text(g_clip_s2c.data, + g_clip_s2c.total_bytes); + } } + } } else if (lxevent->target == XA_STRING) @@ -2026,8 +2046,18 @@ clipboard_event_selection_notify(XEvent *xevent) g_clip_s2c.data = (char *) g_malloc(g_clip_s2c.total_bytes + 1, 0); g_memcpy(g_clip_s2c.data, data, g_clip_s2c.total_bytes); g_clip_s2c.data[g_clip_s2c.total_bytes] = 0; - clipboard_send_data_response_for_text(g_clip_s2c.data, - g_clip_s2c.total_bytes); + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(text) XA_STRING(%s) is restricted because of config", + g_clip_s2c.data); + } + else + { + clipboard_send_data_response_for_text(g_clip_s2c.data, + g_clip_s2c.total_bytes); + } + } } else if (lxevent->target == g_image_bmp_atom) @@ -2037,11 +2067,21 @@ clipboard_event_selection_notify(XEvent *xevent) if ((g_clip_s2c.incr_in_progress == 0) && (data_size > 14)) { g_free(g_clip_s2c.data); - g_clip_s2c.total_bytes = data_size; - g_clip_s2c.data = (char *) g_malloc(data_size, 0); - g_memcpy(g_clip_s2c.data, data, data_size); - clipboard_send_data_response_for_image(g_clip_s2c.data + 14, - data_size - 14); + + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_IMAGE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(image) image/bmp is restricted because of config"); + } + else + { + g_clip_s2c.total_bytes = data_size; + g_clip_s2c.data = (char *) g_malloc(data_size, 0); + g_memcpy(g_clip_s2c.data, data, data_size); + clipboard_send_data_response_for_image(g_clip_s2c.data + 14, + data_size - 14); + } + } } else if (lxevent->target == g_file_atom1) @@ -2055,8 +2095,19 @@ clipboard_event_selection_notify(XEvent *xevent) g_clip_s2c.data = (char *) g_malloc(g_clip_s2c.total_bytes + 1, 0); g_memcpy(g_clip_s2c.data, data, g_clip_s2c.total_bytes); g_clip_s2c.data[g_clip_s2c.total_bytes] = 0; - clipboard_send_data_response_for_file(g_clip_s2c.data, - g_clip_s2c.total_bytes); + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(file) text/uri-list(%s) is restricted because of config", + g_clip_s2c.data); + } + else + { + clipboard_send_data_response_for_file(g_clip_s2c.data, + g_clip_s2c.total_bytes); + + } + } } else if (lxevent->target == g_file_atom2) @@ -2070,8 +2121,18 @@ clipboard_event_selection_notify(XEvent *xevent) g_clip_s2c.data = (char *) g_malloc(g_clip_s2c.total_bytes + 1, 0); g_memcpy(g_clip_s2c.data, data, g_clip_s2c.total_bytes); g_clip_s2c.data[g_clip_s2c.total_bytes] = 0; - clipboard_send_data_response_for_file(g_clip_s2c.data, - g_clip_s2c.total_bytes); + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(file) x-special/gnome-copied-files(%s) is restricted because of config", + g_clip_s2c.data); + } + else + { + clipboard_send_data_response_for_file(g_clip_s2c.data, + g_clip_s2c.total_bytes); + } + } } else @@ -2090,35 +2151,81 @@ clipboard_event_selection_notify(XEvent *xevent) if (got_file_atom != 0) { /* text/uri-list or x-special/gnome-copied-files */ - g_clip_s2c.type = got_file_atom; - g_clip_s2c.xrdp_clip_type = XRDP_CB_FILE; - g_clip_s2c.converted = 0; - g_clip_s2c.clip_time = lxevent->time; - send_format_announce = 1; + + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(file) is restricted because of config"); + } + else + { + g_clip_s2c.type = got_file_atom; + g_clip_s2c.xrdp_clip_type = XRDP_CB_FILE; + g_clip_s2c.converted = 0; + g_clip_s2c.clip_time = lxevent->time; + send_format_announce = 1; + } + } else if (got_utf8) { - g_clip_s2c.type = g_utf8_atom; - g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT; - g_clip_s2c.converted = 0; - g_clip_s2c.clip_time = lxevent->time; - send_format_announce = 1; + + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(text) is restricted because of config"); + } + else + { + g_clip_s2c.type = g_utf8_atom; + g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT; + g_clip_s2c.converted = 0; + g_clip_s2c.clip_time = lxevent->time; + send_format_announce = 1; + } + } else if (got_string) { - g_clip_s2c.type = XA_STRING; - g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT; - g_clip_s2c.converted = 0; - g_clip_s2c.clip_time = lxevent->time; - send_format_announce = 1; + + /* + * In most cases, when copying text, TARGETS atom and UTF8_STRING atom exists, + * it means that this code block which checks STRING atom might not be never executed + * in recent platforms. + * Use echo foo | xclip -selection clipboard -noutf8 to reproduce it. + */ + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_TEXT) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(text) is restricted because of config"); + } + else + { + g_clip_s2c.type = XA_STRING; + g_clip_s2c.xrdp_clip_type = XRDP_CB_TEXT; + g_clip_s2c.converted = 0; + g_clip_s2c.clip_time = lxevent->time; + send_format_announce = 1; + } + } else if (got_bmp_image) { - g_clip_s2c.type = g_image_bmp_atom; - g_clip_s2c.xrdp_clip_type = XRDP_CB_BITMAP; - g_clip_s2c.converted = 0; - g_clip_s2c.clip_time = lxevent->time; - send_format_announce = 1; + + if (g_cfg->restrict_outbound_clipboard & CLIP_RESTRICT_IMAGE) + { + LOG(LOG_LEVEL_DEBUG, + "outbound clipboard(image) is restricted because of config"); + } + else + { + g_clip_s2c.type = g_image_bmp_atom; + g_clip_s2c.xrdp_clip_type = XRDP_CB_BITMAP; + g_clip_s2c.converted = 0; + g_clip_s2c.clip_time = lxevent->time; + send_format_announce = 1; + } + } if (send_format_announce) @@ -2527,15 +2634,7 @@ clipboard_xevent(void *xevent) switch (lxevent->type) { case SelectionNotify: - if (g_cfg->restrict_outbound_clipboard == 0) - { - clipboard_event_selection_notify(lxevent); - } - else - { - LOG(LOG_LEVEL_INFO, "outbound clipboard is restricted because of config"); - return 1; - } + clipboard_event_selection_notify(lxevent); break; case SelectionRequest: clipboard_event_selection_request(lxevent); -- cgit v1.2.3 From 1d6d80d14f5c02bb02288f8c1a7fc220f2391d7c Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 14:45:54 +0900 Subject: Block inbound clipboard text/image/file respectively Disable clipboard_event_selection_request call is overkill for blocking text/image/file purpose. For example, it breaks existing behavior (slow response from gedit, gimp as a side effects) Instead, in clipboard_event_selection_request, these media format will be blocked respectively which depends on the following configurations in sesman.ini [Security] section. * RestrictInboundClipboard=text * RestrictInboundClipboard=file * RestrictInboundClipboard=image You can also set comma separated list. * RestrictInboundClipboard=text,file,image --- sesman/chansrv/clipboard.c | 148 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 30 deletions(-) diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index 5cbaf6f5..e44a2063 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -2350,19 +2350,43 @@ clipboard_event_selection_request(XEvent *xevent) { LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: " "text requested when files available"); - g_memcpy(&g_saved_selection_req_event, lxev, - sizeof(g_saved_selection_req_event)); - g_clip_c2s.type = lxev->target; - g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; - clipboard_send_data_request(g_file_format_id); + + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard %s is restricted because of config", + lxev->target == XA_STRING ? "XA_STRING" : "UTF8_STRING"); + clipboard_refuse_selection(lxev); + } + else + { + g_memcpy(&g_saved_selection_req_event, lxev, + sizeof(g_saved_selection_req_event)); + g_clip_c2s.type = lxev->target; + g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; + clipboard_send_data_request(g_file_format_id); + } + } else { - g_memcpy(&g_saved_selection_req_event, lxev, - sizeof(g_saved_selection_req_event)); - g_clip_c2s.type = lxev->target; - g_clip_c2s.xrdp_clip_type = XRDP_CB_TEXT; - clipboard_send_data_request(CB_FORMAT_UNICODETEXT); + + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_TEXT) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard %s is restricted because of config", + lxev->target == XA_STRING ? "XA_STRING" : "UTF8_STRING"); + clipboard_refuse_selection(lxev); + } + else + { + g_memcpy(&g_saved_selection_req_event, lxev, + sizeof(g_saved_selection_req_event)); + g_clip_c2s.type = lxev->target; + g_clip_c2s.xrdp_clip_type = XRDP_CB_TEXT; + clipboard_send_data_request(CB_FORMAT_UNICODETEXT); + } + } return 0; } @@ -2372,15 +2396,37 @@ clipboard_event_selection_request(XEvent *xevent) if ((g_clip_c2s.type == lxev->target) && g_clip_c2s.converted) { LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: -------------------------------------------"); - clipboard_provide_selection_c2s(lxev, lxev->target); + + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_IMAGE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard image/bmp converted is restricted because of config"); + clipboard_refuse_selection(lxev); + } + else + { + clipboard_provide_selection_c2s(lxev, lxev->target); + } return 0; + + } + + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_IMAGE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard image/bmp is restricted because of config"); + clipboard_refuse_selection(lxev); + } + else + { + g_memcpy(&g_saved_selection_req_event, lxev, + sizeof(g_saved_selection_req_event)); + g_clip_c2s.type = g_image_bmp_atom; + g_clip_c2s.xrdp_clip_type = XRDP_CB_BITMAP; + clipboard_send_data_request(CB_FORMAT_DIB); } - g_memcpy(&g_saved_selection_req_event, lxev, - sizeof(g_saved_selection_req_event)); - g_clip_c2s.type = g_image_bmp_atom; - g_clip_c2s.xrdp_clip_type = XRDP_CB_BITMAP; - clipboard_send_data_request(CB_FORMAT_DIB); return 0; + } else if (lxev->target == g_file_atom1) { @@ -2388,31 +2434,73 @@ clipboard_event_selection_request(XEvent *xevent) if ((g_clip_c2s.type == lxev->target) && g_clip_c2s.converted) { LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: -------------------------------------------"); - clipboard_provide_selection_c2s(lxev, lxev->target); + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard text/uri-list is restricted because of config"); + clipboard_refuse_selection(lxev); + return 0; + } + else + { + clipboard_provide_selection_c2s(lxev, lxev->target); + return 0; + } + } + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard text/uri-list is restricted because of config"); + clipboard_refuse_selection(lxev); return 0; } - g_memcpy(&g_saved_selection_req_event, lxev, - sizeof(g_saved_selection_req_event)); - g_clip_c2s.type = g_file_atom1; - g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; - clipboard_send_data_request(g_file_format_id); - return 0; + else + { + g_memcpy(&g_saved_selection_req_event, lxev, + sizeof(g_saved_selection_req_event)); + g_clip_c2s.type = g_file_atom1; + g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; + clipboard_send_data_request(g_file_format_id); + return 0; + } + } else if (lxev->target == g_file_atom2) { LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: g_file_atom2"); + if ((g_clip_c2s.type == lxev->target) && g_clip_c2s.converted) { LOG_DEVEL(LOG_LEVEL_DEBUG, "clipboard_event_selection_request: -------------------------------------------"); - clipboard_provide_selection_c2s(lxev, lxev->target); + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard x-special/gnome-copied-files converted is restricted because of config"); + clipboard_refuse_selection(lxev); + return 0; + } + else + { + clipboard_provide_selection_c2s(lxev, lxev->target); + return 0; + } + } + if (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) + { + LOG(LOG_LEVEL_DEBUG, + "inbound clipboard x-special/gnome-copied-files is restricted because of config"); + clipboard_refuse_selection(lxev); + return 0; + } + else + { + g_memcpy(&g_saved_selection_req_event, lxev, + sizeof(g_saved_selection_req_event)); + g_clip_c2s.type = g_file_atom2; + g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; + clipboard_send_data_request(g_file_format_id); return 0; } - g_memcpy(&g_saved_selection_req_event, lxev, - sizeof(g_saved_selection_req_event)); - g_clip_c2s.type = g_file_atom2; - g_clip_c2s.xrdp_clip_type = XRDP_CB_FILE; - clipboard_send_data_request(g_file_format_id); - return 0; } else { -- cgit v1.2.3 From 47bc56f5a4678e3f4a7dd83e5bd532cf8c496d66 Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 14:38:52 +0900 Subject: Add sesman.ini new text/file/image restriction settings RestrictInboundClipboard is added. Then, RestrictOutboundClipboard/RestrictInboundClipboard configuration is extended to accept comma separated list. * RestrictOutboundClipboard=none * RestrictOutboundClipboard=text * RestrictOutboundClipboard=file * RestrictOutboundClipboard=image * RestrictOutboundClipboard=all * RestrictOutboundClipboard=text, image, file For compatibility, the following configuration is also accepted (alias) * RestrictOutboundClipboard=true * RestrictOutboundClipboard=false * RestrictOutboundClipboard=yes --- sesman/sesman.ini.in | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/sesman/sesman.ini.in b/sesman/sesman.ini.in index 7b8f0880..8c5f173f 100644 --- a/sesman/sesman.ini.in +++ b/sesman/sesman.ini.in @@ -19,9 +19,24 @@ TerminalServerAdmins=tsadmins ; When AlwaysGroupCheck=false access will be permitted ; if the group TerminalServerUsers is not defined. AlwaysGroupCheck=false -; When RestrictOutboundClipboard=true clipboard from the +; When RestrictOutboundClipboard=all clipboard from the ; server is not pushed to the client. -RestrictOutboundClipboard=false +; In addition, you can control text/file/image transfer restrictions +; respectively. It also accepts comma separated list such as text,file,image. +; To keep compatibility, some aliases are also available: +; true: an alias of all +; false: an alias of none +; yes: an alias of all +RestrictOutboundClipboard=none +; When RestrictInboundClipboard=all clipboard from the +; client is not pushed to the server. +; In addition, you can control text/file/image transfer restrictions +; respectively. It also accepts comma separated list such as text,file,image. +; To keep compatibility, some aliases are also available: +; true: an alias of all +; false: an alias of none +; yes: an alias of all +RestrictInboundClipboard=none [Sessions] ;; X11DisplayOffset - x11 display number offset -- cgit v1.2.3 From 8487c298ba06233bc749ab63f6a3ba80755e0ed7 Mon Sep 17 00:00:00 2001 From: Kentaro Hayashi Date: Tue, 14 Dec 2021 17:59:15 +0900 Subject: Update sesman.ini.5 explanation about RestrictOutboundClipboard,RestrictOutboundClipboard RestrictOutboundClipboard,RestrictOutboundClipboard are extended to accept text,file,image configuration value. --- docs/man/sesman.ini.5.in | 58 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/docs/man/sesman.ini.5.in b/docs/man/sesman.ini.5.in index 0f58c0ce..7291038f 100644 --- a/docs/man/sesman.ini.5.in +++ b/docs/man/sesman.ini.5.in @@ -221,10 +221,62 @@ login for all users is enabled. have session management rights. .TP -\fBRestrictOutboundClipboard\fR=\fI[true|false]\fR -If set to \fB1\fR, \fBtrue\fR or \fByes\fR, will restrict the clipboard +\fBRestrictOutboundClipboard\fR=\fI[all|none|text|file|image]\fR +If set to \fBall\fR, will restrict the clipboard outbound from the server, to prevent data copied inside the xrdp session -to be be pasted in the client host. Default value is \fBfalse\fR. +to be pasted in the client. Default value is \fBnone\fR. +In addition, you can control text/file/image transfer restrictions +respectively. It also accepts comma separated list such as text,file,image. +.br + +.br +\fBnone\fR - No restriction about copying inbound clipboard data. +.br +\fBall\fR - Restrict to copy inbound clipboard data. +.br +\fBtext\fR - Restrict to copy only inbound text clipboard data. +.br +\fBfile\fR - Restrict to copy only inbound file clipboard data. +.br +\fBimage\fR - Restrict to copy only inbound image clipboard data. +.br + +To keep compatibility, the following aliases are also available. +.br +\fBtrue\fR - an alias of \fBall\fR. +.br +\fBfalse\fR - an alias of \fBnone\fR. +.br +\fByes\fR - an alias of \fBall\fR. + +.TP +\fBRestrictInboundClipboard\fR=\fI[none|all|text|file|image]\fR +If set to \fBall\fR, will restrict the clipboard +inbound from the client, to prevent data copied inside the client +to be pasted in the xrdp session. Default value is \fBnone\fR. +In addition, you can control text/file/image transfer restrictions +respectively. It also accepts comma separated list such as text,file,image. +.br + +.br +\fBnone\fR - No restriction about copying inbound clipboard data. +.br +\fBall\fR - Restrict to copy inbound clipboard data. +.br +\fBtext\fR - Restrict to copy only inbound text clipboard data. +.br +\fBfile\fR - Restrict to copy only inbound file clipboard data. +.br +\fBimage\fR - Restrict to copy only inbound image clipboard data. +.br + +To keep compatibility, the following aliases are also available. +.br +\fBtrue\fR - an alias of \fBall\fR. +.br +\fBfalse\fR - an alias of \fBnone\fR. +.br +\fByes\fR - an alias of \fBall\fR. .TP \fBAlwaysGroupCheck\fR=\fI[true|false]\fR -- cgit v1.2.3 From cffce1f8561d9855f031610b1db5040652155d59 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 14 Jan 2022 11:11:03 +0000 Subject: Only advertise X11 clip formats we can supply --- sesman/chansrv/clipboard.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index e44a2063..e2a716ef 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -2294,16 +2294,23 @@ clipboard_event_selection_request(XEvent *xevent) atom_buf[0] = g_targets_atom; atom_buf[1] = g_timestamp_atom; atom_buf[2] = g_multiple_atom; - atom_buf[3] = XA_STRING; - atom_buf[4] = g_utf8_atom; - atom_count = 5; - if (clipboard_find_format_id(CB_FORMAT_DIB) >= 0) + atom_count = 3; + if ((g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_TEXT) == 0) + { + atom_buf[atom_count] = XA_STRING; + atom_count++; + atom_buf[atom_count] = g_utf8_atom; + atom_count++; + } + if (clipboard_find_format_id(CB_FORMAT_DIB) >= 0 && + (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_IMAGE) == 0) { LOG_DEVEL(LOG_LEVEL_DEBUG, " reporting image/bmp"); atom_buf[atom_count] = g_image_bmp_atom; atom_count++; } - if (clipboard_find_format_id(g_file_format_id) >= 0) + if (clipboard_find_format_id(g_file_format_id) >= 0 && + (g_cfg->restrict_inbound_clipboard & CLIP_RESTRICT_FILE) == 0) { LOG_DEVEL(LOG_LEVEL_DEBUG, " reporting text/uri-list"); atom_buf[atom_count] = g_file_atom1; -- cgit v1.2.3 From 307bfc1f4a6c673f297d18e36b567d55add8dff0 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 14 Jan 2022 12:40:22 +0000 Subject: Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1baa2a76..624f3840 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ you would need **openssl-devel**, **pam-devel**, **libX11-devel**, be needed depending on your configuration. To compile xrdp from a checked out git repository, you would additionally -need **autoconf**, **automake**, **libtool** and **pkgconfig**. +need **autoconf**, **automake**, **libtool** and **pkg-config**. ### Get the source and build it -- cgit v1.2.3 From bf4c5f263164c15287d193a147dbd0f0ea5b5ad1 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 17 Jan 2022 10:54:54 +0000 Subject: README : Make latest version dynamic --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 624f3840..e2d05699 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.17 +[![Latest Version](https://img.shields.io/github/v/release/neutrinolabs/xrdp.svg?label=Latest%20Version)](https://github.com/neutrinolabs/xrdp/releases) # xrdp - an open source RDP server -- cgit v1.2.3 From 3146e624c40143ba7f442c7241162505ba08ce03 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 19 Jan 2022 11:08:13 +0000 Subject: Fix problems with check 0.15.2 (F36) --- tests/common/test_os_calls.c | 3 ++- tests/common/test_string_calls.c | 3 ++- tests/libxrdp/test_monitor_processing.c | 3 ++- tests/xrdp/test_bitmap_load.c | 3 ++- tests/xrdp/test_xrdp_main.c | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/common/test_os_calls.c b/tests/common/test_os_calls.c index 1c518e1d..ac785715 100644 --- a/tests/common/test_os_calls.c +++ b/tests/common/test_os_calls.c @@ -4,9 +4,10 @@ #endif #include -#include "test_common.h" #include "os_calls.h" +#include "test_common.h" + #ifndef TOP_SRCDIR #define TOP_SRCDIR "." #endif diff --git a/tests/common/test_string_calls.c b/tests/common/test_string_calls.c index 58dcaea0..1380357c 100644 --- a/tests/common/test_string_calls.c +++ b/tests/common/test_string_calls.c @@ -3,9 +3,10 @@ #include "config_ac.h" #endif -#include "test_common.h" #include "string_calls.h" +#include "test_common.h" + #define RESULT_LEN 1024 START_TEST(test_strnjoin__when_src_is_null__returns_empty_string) diff --git a/tests/libxrdp/test_monitor_processing.c b/tests/libxrdp/test_monitor_processing.c index 969da780..951ffe68 100644 --- a/tests/libxrdp/test_monitor_processing.c +++ b/tests/libxrdp/test_monitor_processing.c @@ -2,10 +2,11 @@ #include "config_ac.h" #endif -#include "test_libxrdp.h" #include "libxrdp.h" #include "os_calls.h" +#include "test_libxrdp.h" + struct xrdp_sec *sec_layer; struct xrdp_rdp *rdp_layer; struct xrdp_session *session; diff --git a/tests/xrdp/test_bitmap_load.c b/tests/xrdp/test_bitmap_load.c index 42b5a325..5c25504e 100644 --- a/tests/xrdp/test_bitmap_load.c +++ b/tests/xrdp/test_bitmap_load.c @@ -2,9 +2,10 @@ #include "config_ac.h" #endif -#include "test_xrdp.h" #include "xrdp.h" +#include "test_xrdp.h" + /* Where the image files are located */ #ifndef IMAGEDIR #define IMAGEDIR "." diff --git a/tests/xrdp/test_xrdp_main.c b/tests/xrdp/test_xrdp_main.c index eb118a1e..d56dcb37 100644 --- a/tests/xrdp/test_xrdp_main.c +++ b/tests/xrdp/test_xrdp_main.c @@ -33,9 +33,10 @@ #include "log.h" #include "os_calls.h" -#include "test_xrdp.h" #include +#include "test_xrdp.h" + int main (void) { int number_failed; -- cgit v1.2.3 From d853228c1927d48fb1ec3181b50a6a481448a049 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Tue, 18 Jan 2022 16:30:52 +0000 Subject: const fixes for SSL calls --- common/ssl_calls.c | 4 ++-- common/ssl_calls.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common/ssl_calls.c b/common/ssl_calls.c index b305815f..30640c51 100644 --- a/common/ssl_calls.c +++ b/common/ssl_calls.c @@ -140,7 +140,7 @@ ssl_rc4_info_delete(void *rc4_info) /*****************************************************************************/ void -ssl_rc4_set_key(void *rc4_info, char *key, int len) +ssl_rc4_set_key(void *rc4_info, const char *key, int len) { RC4_set_key((RC4_KEY *)rc4_info, len, (tui8 *)key); } @@ -214,7 +214,7 @@ ssl_md5_clear(void *md5_info) /*****************************************************************************/ void -ssl_md5_transform(void *md5_info, char *data, int len) +ssl_md5_transform(void *md5_info, const char *data, int len) { MD5_Update((MD5_CTX *)md5_info, data, len); } diff --git a/common/ssl_calls.h b/common/ssl_calls.h index 121cb390..78edd946 100644 --- a/common/ssl_calls.h +++ b/common/ssl_calls.h @@ -31,7 +31,7 @@ ssl_rc4_info_create(void); void ssl_rc4_info_delete(void *rc4_info); void -ssl_rc4_set_key(void *rc4_info, char *key, int len); +ssl_rc4_set_key(void *rc4_info, const char *key, int len); void ssl_rc4_crypt(void *rc4_info, char *data, int len); void * @@ -51,7 +51,7 @@ ssl_md5_info_delete(void *md5_info); void ssl_md5_clear(void *md5_info); void -ssl_md5_transform(void *md5_info, char *data, int len); +ssl_md5_transform(void *md5_info, const char *data, int len); void ssl_md5_complete(void *md5_info, char *data); void * -- cgit v1.2.3 From d02059d967f0bc1deaf915d3971d4aa8c41dbb4e Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:43:00 +0000 Subject: Add missing ssl_sha1_clear()/ssl_md5_clear() calls --- sesman/env.c | 1 + vnc/vnc.c | 1 + xrdp/xrdp_bitmap.c | 1 + 3 files changed, 3 insertions(+) diff --git a/sesman/env.c b/sesman/env.c index cdac0f17..b8b2c030 100644 --- a/sesman/env.c +++ b/sesman/env.c @@ -55,6 +55,7 @@ env_check_password_file(const char *filename, const char *passwd) /* create password hash from password */ passwd_bytes = g_strlen(passwd); sha1 = ssl_sha1_info_create(); + ssl_sha1_clear(sha1); ssl_sha1_transform(sha1, "xrdp_vnc", 8); ssl_sha1_transform(sha1, passwd, passwd_bytes); ssl_sha1_transform(sha1, passwd, passwd_bytes); diff --git a/vnc/vnc.c b/vnc/vnc.c index 81e5ecd0..74861329 100644 --- a/vnc/vnc.c +++ b/vnc/vnc.c @@ -94,6 +94,7 @@ rfbHashEncryptBytes(char *bytes, const char *passwd) /* create password hash from password */ passwd_bytes = g_strlen(passwd); sha1 = ssl_sha1_info_create(); + ssl_sha1_clear(sha1); ssl_sha1_transform(sha1, "xrdp_vnc", 8); ssl_sha1_transform(sha1, passwd, passwd_bytes); ssl_sha1_transform(sha1, passwd, passwd_bytes); diff --git a/xrdp/xrdp_bitmap.c b/xrdp/xrdp_bitmap.c index efab0621..72aafff2 100644 --- a/xrdp/xrdp_bitmap.c +++ b/xrdp/xrdp_bitmap.c @@ -174,6 +174,7 @@ xrdp_bitmap_hash_crc(struct xrdp_bitmap *self) return 1; } hash = ssl_md5_info_create(); + ssl_md5_clear(hash); ssl_md5_transform(hash, self->data, bytes); ssl_md5_complete(hash, hash_data); ssl_md5_info_delete(hash); -- cgit v1.2.3 From fde161bac3eebc73b2e7c7bba4ab9381ef5327b9 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:44:49 +0000 Subject: Add unit tests for SSL calls --- tests/common/Makefile.am | 3 +- tests/common/test_common.h | 1 + tests/common/test_common_main.c | 18 +- tests/common/test_ssl_calls.c | 417 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 437 insertions(+), 2 deletions(-) create mode 100644 tests/common/test_ssl_calls.c diff --git a/tests/common/Makefile.am b/tests/common/Makefile.am index 5a5ca8d5..88548b71 100644 --- a/tests/common/Makefile.am +++ b/tests/common/Makefile.am @@ -13,7 +13,8 @@ test_common_SOURCES = \ test_common.h \ test_common_main.c \ test_string_calls.c \ - test_os_calls.c + test_os_calls.c \ + test_ssl_calls.c test_common_CFLAGS = \ @CHECK_CFLAGS@ \ diff --git a/tests/common/test_common.h b/tests/common/test_common.h index b9db8373..3493c3bf 100644 --- a/tests/common/test_common.h +++ b/tests/common/test_common.h @@ -6,5 +6,6 @@ Suite *make_suite_test_string(void); Suite *make_suite_test_os_calls(void); +Suite *make_suite_test_ssl_calls(void); #endif /* TEST_COMMON_H */ diff --git a/tests/common/test_common_main.c b/tests/common/test_common_main.c index 694a827a..2674bea0 100644 --- a/tests/common/test_common_main.c +++ b/tests/common/test_common_main.c @@ -4,7 +4,10 @@ #endif #include -#include + +#include "log.h" +#include "ssl_calls.h" + #include "test_common.h" int main (void) @@ -14,11 +17,24 @@ int main (void) sr = srunner_create (make_suite_test_string()); srunner_add_suite(sr, make_suite_test_os_calls()); + srunner_add_suite(sr, make_suite_test_ssl_calls()); // srunner_add_suite(sr, make_list_suite()); srunner_set_tap(sr, "-"); + /* + * Set up console logging */ + struct log_config *lc = log_config_init_for_console(LOG_LEVEL_INFO, NULL); + log_start_from_param(lc); + log_config_free(lc); + + /* Initialise the ssl module */ + ssl_init(); + srunner_run_all (sr, CK_ENV); number_failed = srunner_ntests_failed(sr); srunner_free(sr); + + ssl_finish(); + log_end(); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/tests/common/test_ssl_calls.c b/tests/common/test_ssl_calls.c new file mode 100644 index 00000000..c5198fd9 --- /dev/null +++ b/tests/common/test_ssl_calls.c @@ -0,0 +1,417 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "os_calls.h" +#include "string_calls.h" +#include "ssl_calls.h" + +#include "test_common.h" + +static +char *bin_to_hex(const char *input, int length) +{ + int i; + char *result = (char *)g_malloc(length * 2 + 1, 0); + if (result != NULL) + { + char *p = result; + const char *hexdigit = "0123456789abcdef"; + for (i = 0 ; i < length ; ++i) + { + int c = input[i]; + if (c < 0) + { + c += 256; + } + *p++ = hexdigit[ c / 16]; + *p++ = hexdigit[ c % 16]; + } + *p = '\0'; + } + + return result; +} + + +START_TEST(test_rc4_enc_ok) +{ + const char *key = "16_byte_key-----"; + char text[] = "xrdp-test-suite-rc4-encryption"; + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, key, g_strlen(key)); + ssl_rc4_crypt(info, text, sizeof(text) - 1); + ssl_rc4_info_delete(info); + result = bin_to_hex(text, sizeof(text) - 1); + ck_assert(result != NULL); + /* Result should be the same as + * echo -n '' | \ + * openssl rc4 -K -e [-provider legacy] | \ + * xxd -g0 + * + * where is the string above in hexadecimal */ + ck_assert_str_eq(result, "c080f175b2d85802dbf1042f07180ddc4be1d9bd4a44158f0aebf11c961b"); + g_free(result); +} +END_TEST + +START_TEST(test_rc4_enc_tv0_ok) +{ + /* + * This is one of the 5 original RC4 test vectors posted in response to + * the 'RC4 Algorithm revealed' sci.crypt usenet posting */ + unsigned char key[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + unsigned char text[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + const char *expected = "75b7878099e0c596"; + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, (char *)key, sizeof(key)); + ssl_rc4_crypt(info, (char *)text, sizeof(text)); + ssl_rc4_info_delete(info); + + result = bin_to_hex((char *)text, sizeof(text)); + ck_assert(result != NULL); + ck_assert_str_eq(result, expected); + g_free(result); +} +END_TEST + +START_TEST(test_rc4_enc_tv1_ok) +{ + /* + * This is one of the 5 original RC4 test vectors posted in response to + * the 'RC4 Algorithm revealed' sci.crypt usenet posting */ + unsigned char key[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + unsigned char text[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const char *expected = "7494c2e7104b0879"; + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, (char *)key, sizeof(key)); + ssl_rc4_crypt(info, (char *)text, sizeof(text)); + ssl_rc4_info_delete(info); + + result = bin_to_hex((char *)text, sizeof(text)); + ck_assert(result != NULL); + ck_assert_str_eq(result, expected); + g_free(result); +} +END_TEST + +START_TEST(test_rc4_enc_tv2_ok) +{ + /* + * This is one of the 5 original RC4 test vectors posted in response to + * the 'RC4 Algorithm revealed' sci.crypt usenet posting */ + unsigned char key[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + unsigned char text[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const char *expected = "de188941a3375d3a"; + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, (char *)key, sizeof(key)); + ssl_rc4_crypt(info, (char *)text, sizeof(text)); + ssl_rc4_info_delete(info); + + result = bin_to_hex((char *)text, sizeof(text)); + ck_assert(result != NULL); + ck_assert_str_eq(result, expected); + g_free(result); +} +END_TEST + +START_TEST(test_rc4_enc_tv3_ok) +{ + /* + * This is one of the 5 original RC4 test vectors posted in response to + * the 'RC4 Algorithm revealed' sci.crypt usenet posting */ + unsigned char key[] = {0xef, 0x01, 0x23, 0x45}; + unsigned char text[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + const char *expected = "d6a141a7ec3c38dfbd61"; + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, (char *)key, sizeof(key)); + ssl_rc4_crypt(info, (char *)text, sizeof(text)); + ssl_rc4_info_delete(info); + + result = bin_to_hex((char *)text, sizeof(text)); + ck_assert(result != NULL); + ck_assert_str_eq(result, expected); + g_free(result); +} +END_TEST + +START_TEST(test_rc4_enc_tv4_ok) +{ + /* + * This is one of the 5 original RC4 test vectors posted in response to + * the 'RC4 Algorithm revealed' sci.crypt usenet posting */ + unsigned char key[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}; + unsigned char text[] = + { + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01 + }; + const char *expected = + "7595c3e6114a09780c4ad452338e1ffd9a1be9498f813d76533449b6778dca" + "d8c78a8d2ba9ac66085d0e53d59c26c2d1c490c1ebbe0ce66d1b6b1b13b6" + "b919b847c25a91447a95e75e4ef16779cde8bf0a95850e32af9689444fd3" + "77108f98fdcbd4e726567500990bcc7e0ca3c4aaa304a387d20f3b8fbbcd" + "42a1bd311d7a4303dda5ab078896ae80c18b0af66dff319616eb784e495a" + "d2ce90d7f772a81747b65f62093b1e0db9e5ba532fafec47508323e67132" + "7df9444432cb7367cec82f5d44c0d00b67d650a075cd4b70dedd77eb9b10" + "231b6b5b741347396d62897421d43df9b42e446e358e9c11a9b2184ecbef" + "0cd8e7a877ef968f1390ec9b3d35a5585cb009290e2fcde7b5ec66d9084b" + "e44055a619d9dd7fc3166f9487f7cb272912426445998514c15d53a18c86" + "4ce3a2b7555793988126520eacf2e3066e230c91bee4dd5304f5fd0405b3" + "5bd99c73135d3d9bc335ee049ef69b3867bf2d7bd1eaa595d8bfc0066ff8" + "d31509eb0c6caa006c807a623ef84c3d33c195d23ee320c40de0558157c8" + "22d4b8c569d849aed59d4e0fd7f379586b4b7ff684ed6a189f7486d49b9c" + "4bad9ba24b96abf924372c8a8fffb10d55354900a77a3db5f205e1b99fcd" + "8660863a159ad4abe40fa48934163ddde542a6585540fd683cbfd8c00f12" + "129a284deacc4cdefe58be7137541c047126c8d49e2755ab181ab7e940b0c0"; + + char *result; + + void *info = ssl_rc4_info_create(); + ssl_rc4_set_key(info, (char *)key, sizeof(key)); + ssl_rc4_crypt(info, (char *)text, sizeof(text)); + ssl_rc4_info_delete(info); + + result = bin_to_hex((char *)text, sizeof(text)); + ck_assert(result != NULL); + ck_assert_str_eq(result, expected); + g_free(result); +} +END_TEST + +START_TEST(test_sha1_hash_ok) +{ + const char *hash_string = "xrdp-test-suite-sha1-hash"; + char digest[20]; + char *result1; + char *result2; + + void *info = ssl_sha1_info_create(); + ssl_sha1_clear(info); + ssl_sha1_transform(info, hash_string, g_strlen(hash_string)); + ssl_sha1_complete(info, digest); + result1 = bin_to_hex(digest, sizeof(digest)); + ck_assert(result1 != NULL); + /* Check result with echo -n '' | sha1sum */ + ck_assert_str_eq(result1, "3ea0ae84e97e6262c7cfe79ccd7ad2094c06885d"); + + /* Check a clear has the desired effect */ + ssl_sha1_clear(info); + ssl_sha1_transform(info, hash_string, g_strlen(hash_string)); + ssl_sha1_complete(info, digest); + result2 = bin_to_hex(digest, sizeof(digest)); + ck_assert(result2 != NULL); + ck_assert_str_eq(result1, result2); + + ssl_sha1_info_delete(info); + g_free(result1); + g_free(result2); +} +END_TEST + +START_TEST(test_md5_hash_ok) +{ + const char *hash_string = "xrdp-test-suite-md5-hash"; + char digest[16]; + char *result1; + char *result2; + + void *info = ssl_md5_info_create(); + + ssl_md5_clear(info); + ssl_md5_transform(info, hash_string, g_strlen(hash_string)); + ssl_md5_complete(info, digest); + result1 = bin_to_hex(digest, sizeof(digest)); + ck_assert(result1 != NULL); + /* Check result with echo -n '' | md5sum */ + ck_assert_str_eq(result1, "ddc599dc7ec62b8f78760b071704c007"); + + /* Check a clear has the desired effect */ + ssl_md5_clear(info); + ssl_md5_transform(info, hash_string, g_strlen(hash_string)); + ssl_md5_complete(info, digest); + result2 = bin_to_hex(digest, sizeof(digest)); + ck_assert(result2 != NULL); + ck_assert_str_eq(result1, result2); + + ssl_md5_info_delete(info); + g_free(result1); + g_free(result2); +} +END_TEST + +START_TEST(test_des3_enc_ok) +{ + const char *key = "24_byte_key-------------"; + char plaintext[] = "xrdp-test-suite-des3-encryption-must-be-multiple-of-8-chars-long--------"; + char ciphertext[sizeof(plaintext) - 1]; /* No terminator needed */ + char plaintext2[sizeof(plaintext)]; + + char *result; + + void *info = ssl_des3_encrypt_info_create(key, 0); + ssl_des3_encrypt(info, sizeof(plaintext) - 1, plaintext, ciphertext); + ssl_des3_info_delete(info); + result = bin_to_hex(ciphertext, sizeof(ciphertext)); + ck_assert(result != NULL); + /* Result should be the same as + * echo -n '' | \ + * openssl des3 -iv 0000000000000000 -K -e -nopad | \ + * od -t x1 + * + * where is the string above in hexadecimal */ + ck_assert_str_eq(result, + "856d70861827365e188781616e4f9dcc3009b2c5dc7785edcbc05fa825a4ea5e10b23735c0e971ca20f895f455b8845418963af6dd8e649719790eed6cbcee0fb97b743c60e32e8b"); + g_free(result); + + /* Let's go back again */ + info = ssl_des3_decrypt_info_create(key, 0); + ssl_des3_decrypt(info, sizeof(ciphertext), ciphertext, plaintext2); + ssl_des3_info_delete(info); + plaintext2[sizeof(plaintext2) - 1] = '\0'; + + ck_assert_str_eq(plaintext, plaintext2); +} +END_TEST + +START_TEST(test_hmac_sha1_dgst_ok) +{ + const char *key = "20_byte_key---------"; + const char *hash_string = "xrdp-test-suite-hmac-sha1-dgst"; + char hmac[20]; + + char *result; + + void *info = ssl_hmac_info_create(); + ssl_hmac_sha1_init(info, key, g_strlen(key)); + ssl_hmac_transform(info, hash_string, g_strlen(hash_string)); + ssl_hmac_complete(info, hmac, sizeof(hmac)); + ssl_hmac_info_delete(info); + result = bin_to_hex(hmac, sizeof(hmac)); + ck_assert(result != NULL); + /* Result should be the same as + * echo -n '' | openssl dgst -sha1 -hmac '' + * + * or:- + * + * echo -n '' | openssl mac -digest sha1 -macopt key:'' hmac + */ + ck_assert_str_eq(result, "af8c04e609e9f3cba53ad7815b60160dc69a9936"); + g_free(result); + +} +END_TEST + +START_TEST(test_gen_key_xrdp1) +{ +#define RSA_TEST_BITS 2048 + char modulus[RSA_TEST_BITS / 8] = {0}; + char private_key[RSA_TEST_BITS / 8] = {0}; + unsigned char exponent[4] = + { + 0x01, 0x00, 0x01, 0x00 /* 65537 in little-endian format */ + }; + + /* + * We can't do much here because of the nature of the call, but we can + * at least check it completes without error */ + int error; + error = ssl_gen_key_xrdp1(RSA_TEST_BITS, + (const char *)exponent, sizeof(exponent), + modulus, sizeof(modulus), + private_key, sizeof(private_key)); + ck_assert(error == 0); + + /* Both the modulus and the privatekey should be odd */ + ck_assert((modulus[0] & 1) == 1); + ck_assert((private_key[0] & 1) == 1); +#undef RSA_TEST_BITS +} +END_TEST + +/******************************************************************************/ +Suite * +make_suite_test_ssl_calls(void) +{ + Suite *s; + TCase *tc_ssl_calls; + + s = suite_create("SSL-Calls"); + + tc_ssl_calls = tcase_create("ssl_calls"); + suite_add_tcase(s, tc_ssl_calls); + tcase_add_test(tc_ssl_calls, test_rc4_enc_ok); + tcase_add_test(tc_ssl_calls, test_rc4_enc_tv0_ok); + tcase_add_test(tc_ssl_calls, test_rc4_enc_tv1_ok); + tcase_add_test(tc_ssl_calls, test_rc4_enc_tv2_ok); + tcase_add_test(tc_ssl_calls, test_rc4_enc_tv3_ok); + tcase_add_test(tc_ssl_calls, test_rc4_enc_tv4_ok); + tcase_add_test(tc_ssl_calls, test_sha1_hash_ok); + tcase_add_test(tc_ssl_calls, test_md5_hash_ok); + tcase_add_test(tc_ssl_calls, test_des3_enc_ok); + tcase_add_test(tc_ssl_calls, test_hmac_sha1_dgst_ok); + tcase_add_test(tc_ssl_calls, test_gen_key_xrdp1); + + return s; +} -- cgit v1.2.3 From 6cebade78e15ac5fdb5c807220291ef19ed41a5b Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:45:25 +0000 Subject: OpenSSL 3.x compatibility --- common/ssl_calls.c | 406 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 370 insertions(+), 36 deletions(-) diff --git a/common/ssl_calls.c b/common/ssl_calls.c index 30640c51..792a9130 100644 --- a/common/ssl_calls.c +++ b/common/ssl_calls.c @@ -44,6 +44,16 @@ #define SSL_WANT_READ_WRITE_TIMEOUT 100 +/* + * Globals used by openssl 3 and later */ +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +static EVP_MD *g_md_md5; /* MD5 message digest */ +static EVP_MD *g_md_sha1; /* SHA1 message digest */ +static EVP_CIPHER *g_cipher_des_ede3_cbc; /* DES3 CBC cipher */ +static EVP_MAC *g_mac_hmac; /* HMAC MAC */ +#endif + + #if OPENSSL_VERSION_NUMBER < 0x10100000L static inline HMAC_CTX * HMAC_CTX_new(void) @@ -106,12 +116,41 @@ DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g) #endif /* OPENSSL_VERSION_NUMBER >= 0x10100000L */ + +/*****************************************************************************/ +static void +dump_error_stack(const char *prefix) +{ + /* Dump the error stack from the SSL library */ + unsigned long code; + char buff[256]; + while ((code = ERR_get_error()) != 0L) + { + ERR_error_string_n(code, buff, sizeof(buff)); + LOG(LOG_LEVEL_ERROR, "%s: %s", prefix, buff); + } +} + +/*****************************************************************************/ +/* As above, but used for TLS connection errors where only the first + error is logged */ +static void +dump_ssl_error_stack(struct ssl_tls *self) +{ + if (!self->error_logged) + { + dump_error_stack("SSL"); + self->error_logged = 1; + } +} + /*****************************************************************************/ int ssl_init(void) { SSL_load_error_strings(); SSL_library_init(); + return 0; } @@ -119,16 +158,44 @@ ssl_init(void) int ssl_finish(void) { +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* De-allocate any allocated globals + * For OpenSSL 3, these can all safely be passed a NULL pointer */ + EVP_MD_free(g_md_md5); + EVP_MD_free(g_md_sha1); + EVP_CIPHER_free(g_cipher_des_ede3_cbc); + EVP_MAC_free(g_mac_hmac); +#endif return 0; } -/* rc4 stuff */ +/* rc4 stuff + * + * For OpenSSL 3.0, the rc4 encryption algorithm is only provided by the + * legacy provider (see crypto(7)). Since RC4 is so simple, we can implement + * it directly rather than having to load the legacy provider. This will + * avoids problems if running on a system where openssl has been built + * without the legacy provider */ + +#if OPENSSL_VERSION_NUMBER >= 0x30000000L +struct rc4_data +{ + /* See https://en.wikipedia.org/wiki/RC4 */ + unsigned char S[256]; + int i; + int j; +}; +#endif /*****************************************************************************/ void * ssl_rc4_info_create(void) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L return g_malloc(sizeof(RC4_KEY), 1); +#else + return g_malloc(sizeof(struct rc4_data), 1); +#endif } /*****************************************************************************/ @@ -142,14 +209,75 @@ ssl_rc4_info_delete(void *rc4_info) void ssl_rc4_set_key(void *rc4_info, const char *key, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L RC4_set_key((RC4_KEY *)rc4_info, len, (tui8 *)key); +#else + unsigned char *S = ((struct rc4_data *)rc4_info)->S; + int i; + int j = 0; + unsigned char t; + for (i = 0 ; i < 256; ++i) + { + S[i] = i; + } + for (i = 0 ; i < 256; ++i) + { + j = (j + S[i] + key[i % len]) & 0xff; + t = S[i]; + S[i] = S[j]; + S[j] = t; + } + ((struct rc4_data *)rc4_info)->i = 0; + ((struct rc4_data *)rc4_info)->j = 0; +#endif } /*****************************************************************************/ void ssl_rc4_crypt(void *rc4_info, char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L RC4((RC4_KEY *)rc4_info, len, (tui8 *)data, (tui8 *)data); +#else + unsigned char *S = ((struct rc4_data *)rc4_info)->S; + int i = ((struct rc4_data *)rc4_info)->i; + int j = ((struct rc4_data *)rc4_info)->j; + unsigned char *p = (unsigned char *)data; + unsigned char t; + unsigned char k; + + /* + * Do some loop-unrolling for performance. Here are the steps + * for each byte */ +#define RC4_ROUND \ + i = (i + 1) & 0xff; \ + j = (j + S[i]) & 0xff; \ + t = S[i]; \ + S[i] = S[j]; \ + S[j] = t; \ + k = S[(S[i] + S[j]) & 0xff]; \ + *p++ ^= k + + while (len >= 8) + { + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + RC4_ROUND; + len -= 8; + } + while (len-- > 0) + { + RC4_ROUND; + } + + ((struct rc4_data *)rc4_info)->i = i; + ((struct rc4_data *)rc4_info)->j = j; +#endif } /* sha1 stuff */ @@ -158,35 +286,78 @@ ssl_rc4_crypt(void *rc4_info, char *data, int len) void * ssl_sha1_info_create(void) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L return g_malloc(sizeof(SHA_CTX), 1); +#else + /* + * If we can't get the digest loaded, there's a problem with the + * library providers, so there's no point in us returning anything useful. + * If we do load the digest, it's used later */ + if (g_md_sha1 == NULL) + { + if ((g_md_sha1 = EVP_MD_fetch(NULL, "sha1", NULL)) == NULL) + { + dump_error_stack("sha1"); + return NULL; + } + } + + return (void *)EVP_MD_CTX_new(); +#endif } /*****************************************************************************/ void ssl_sha1_info_delete(void *sha1_info) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L g_free(sha1_info); +#else + EVP_MD_CTX_free((EVP_MD_CTX *)sha1_info); +#endif } /*****************************************************************************/ void ssl_sha1_clear(void *sha1_info) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L SHA1_Init((SHA_CTX *)sha1_info); +#else + if (sha1_info != NULL) + { + EVP_DigestInit_ex(sha1_info, g_md_sha1, NULL); + } +#endif } /*****************************************************************************/ void ssl_sha1_transform(void *sha1_info, const char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L SHA1_Update((SHA_CTX *)sha1_info, data, len); +#else + if (sha1_info != NULL) + { + EVP_DigestUpdate((EVP_MD_CTX *)sha1_info, data, len); + } +#endif } /*****************************************************************************/ void ssl_sha1_complete(void *sha1_info, char *data) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L SHA1_Final((tui8 *)data, (SHA_CTX *)sha1_info); +#else + if (sha1_info != NULL) + { + EVP_DigestFinal_ex((EVP_MD_CTX *)sha1_info, (unsigned char *)data, + NULL); + } +#endif } /* md5 stuff */ @@ -195,35 +366,77 @@ ssl_sha1_complete(void *sha1_info, char *data) void * ssl_md5_info_create(void) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L return g_malloc(sizeof(MD5_CTX), 1); +#else + /* + * If we can't get the digest loaded, there's a problem with the + * library providers, so there's no point in us returning anything useful. + * If we do load the digest, it's used later */ + if (g_md_md5 == NULL) + { + if ((g_md_md5 = EVP_MD_fetch(NULL, "md5", NULL)) == NULL) + { + dump_error_stack("md5"); + return NULL; + } + } + + return (void *)EVP_MD_CTX_new(); +#endif } /*****************************************************************************/ void ssl_md5_info_delete(void *md5_info) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L g_free(md5_info); +#else + EVP_MD_CTX_free((EVP_MD_CTX *)md5_info); +#endif } /*****************************************************************************/ void ssl_md5_clear(void *md5_info) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L MD5_Init((MD5_CTX *)md5_info); +#else + if (md5_info != NULL) + { + EVP_DigestInit_ex(md5_info, g_md_md5, NULL); + } +#endif } /*****************************************************************************/ void ssl_md5_transform(void *md5_info, const char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L MD5_Update((MD5_CTX *)md5_info, data, len); +#else + if (md5_info != NULL) + { + EVP_DigestUpdate((EVP_MD_CTX *)md5_info, data, len); + } +#endif } /*****************************************************************************/ void ssl_md5_complete(void *md5_info, char *data) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L MD5_Final((tui8 *)data, (MD5_CTX *)md5_info); +#else + if (md5_info != NULL) + { + EVP_DigestFinal_ex((EVP_MD_CTX *)md5_info, (unsigned char *)data, NULL); + } +#endif } /* FIPS stuff */ @@ -236,10 +449,30 @@ ssl_des3_encrypt_info_create(const char *key, const char *ivec) const tui8 *lkey; const tui8 *livec; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* + * For these versions of OpenSSL, there are no long-term guarantees the + * DES3 cipher will be available. We'll try to load it here so we + * can log any errors */ + if (g_cipher_des_ede3_cbc == NULL) + { + g_cipher_des_ede3_cbc = EVP_CIPHER_fetch(NULL, "des-ede3-cbc", NULL); + if (g_cipher_des_ede3_cbc == NULL) + { + dump_error_stack("DES-EDE3-CBC"); + return NULL; + } + } +#endif + des3_ctx = EVP_CIPHER_CTX_new(); lkey = (const tui8 *) key; livec = (const tui8 *) ivec; +#if OPENSSL_VERSION_NUMBER < 0x30000000L EVP_EncryptInit_ex(des3_ctx, EVP_des_ede3_cbc(), NULL, lkey, livec); +#else + EVP_EncryptInit_ex(des3_ctx, g_cipher_des_ede3_cbc, NULL, lkey, livec); +#endif EVP_CIPHER_CTX_set_padding(des3_ctx, 0); return des3_ctx; } @@ -252,10 +485,30 @@ ssl_des3_decrypt_info_create(const char *key, const char *ivec) const tui8 *lkey; const tui8 *livec; +#if OPENSSL_VERSION_NUMBER >= 0x30000000L + /* + * For these versions of OpenSSL, there are no long-term guarantees the + * DES3 cipher will be available. We'll try to load it here so we + * can log any errors */ + if (g_cipher_des_ede3_cbc == NULL) + { + g_cipher_des_ede3_cbc = EVP_CIPHER_fetch(NULL, "des-ede3-cbc", NULL); + if (g_cipher_des_ede3_cbc == NULL) + { + dump_error_stack("DES-EDE3-CBC"); + return NULL; + } + } +#endif + des3_ctx = EVP_CIPHER_CTX_new(); lkey = (const tui8 *) key; livec = (const tui8 *) ivec; +#if OPENSSL_VERSION_NUMBER < 0x30000000L EVP_DecryptInit_ex(des3_ctx, EVP_des_ede3_cbc(), NULL, lkey, livec); +#else + EVP_DecryptInit_ex(des3_ctx, g_cipher_des_ede3_cbc, NULL, lkey, livec); +#endif EVP_CIPHER_CTX_set_padding(des3_ctx, 0); return des3_ctx; } @@ -283,10 +536,13 @@ ssl_des3_encrypt(void *des3, int length, const char *in_data, char *out_data) tui8 *lout_data; des3_ctx = (EVP_CIPHER_CTX *) des3; - lin_data = (const tui8 *) in_data; - lout_data = (tui8 *) out_data; - len = 0; - EVP_EncryptUpdate(des3_ctx, lout_data, &len, lin_data, length); + if (des3_ctx != NULL) + { + lin_data = (const tui8 *) in_data; + lout_data = (tui8 *) out_data; + len = 0; + EVP_EncryptUpdate(des3_ctx, lout_data, &len, lin_data, length); + } return 0; } @@ -300,10 +556,13 @@ ssl_des3_decrypt(void *des3, int length, const char *in_data, char *out_data) tui8 *lout_data; des3_ctx = (EVP_CIPHER_CTX *) des3; - lin_data = (const tui8 *) in_data; - lout_data = (tui8 *) out_data; - len = 0; - EVP_DecryptUpdate(des3_ctx, lout_data, &len, lin_data, length); + if (des3_ctx != NULL) + { + lin_data = (const tui8 *) in_data; + lout_data = (tui8 *) out_data; + len = 0; + EVP_DecryptUpdate(des3_ctx, lout_data, &len, lin_data, length); + } return 0; } @@ -311,16 +570,28 @@ ssl_des3_decrypt(void *des3, int length, const char *in_data, char *out_data) void * ssl_hmac_info_create(void) { - HMAC_CTX *hmac_ctx; +#if OPENSSL_VERSION_NUMBER < 0x30000000L + return (HMAC_CTX *)HMAC_CTX_new(); +#else + /* Need a MAC algorithm loaded */ + if (g_mac_hmac == NULL) + { + if ((g_mac_hmac = EVP_MAC_fetch(NULL, "hmac", NULL)) == NULL) + { + dump_error_stack("hmac"); + return NULL; + } + } - hmac_ctx = HMAC_CTX_new(); - return hmac_ctx; + return (void *)EVP_MAC_CTX_new(g_mac_hmac); +#endif } /*****************************************************************************/ void ssl_hmac_info_delete(void *hmac) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L HMAC_CTX *hmac_ctx; hmac_ctx = (HMAC_CTX *) hmac; @@ -328,34 +599,61 @@ ssl_hmac_info_delete(void *hmac) { HMAC_CTX_free(hmac_ctx); } +#else + EVP_MAC_CTX_free((EVP_MAC_CTX *)hmac); +#endif } /*****************************************************************************/ void ssl_hmac_sha1_init(void *hmac, const char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L HMAC_CTX *hmac_ctx; hmac_ctx = (HMAC_CTX *) hmac; HMAC_Init_ex(hmac_ctx, data, len, EVP_sha1(), NULL); +#else + if (hmac != NULL) + { + char digest[] = "sha1"; + OSSL_PARAM params[3]; + size_t n = 0; + params[n++] = OSSL_PARAM_construct_utf8_string("digest", digest, 0); + params[n++] = OSSL_PARAM_construct_end(); + if (EVP_MAC_init((EVP_MAC_CTX *)hmac, (unsigned char *)data, + len, params) == 0) + { + dump_error_stack("hmac-sha1"); + } + } +#endif } /*****************************************************************************/ void ssl_hmac_transform(void *hmac, const char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L HMAC_CTX *hmac_ctx; const tui8 *ldata; hmac_ctx = (HMAC_CTX *) hmac; ldata = (const tui8 *) data; HMAC_Update(hmac_ctx, ldata, len); +#else + if (hmac != NULL) + { + EVP_MAC_update((EVP_MAC_CTX *)hmac, (unsigned char *)data, len); + } +#endif } /*****************************************************************************/ void ssl_hmac_complete(void *hmac, char *data, int len) { +#if OPENSSL_VERSION_NUMBER < 0x30000000L HMAC_CTX *hmac_ctx; tui8 *ldata; tui32 llen; @@ -364,6 +662,12 @@ ssl_hmac_complete(void *hmac, char *data, int len) ldata = (tui8 *) data; llen = len; HMAC_Final(hmac_ctx, ldata, &llen); +#else + if (hmac != NULL) + { + EVP_MAC_final((EVP_MAC_CTX *)hmac, (unsigned char *)data, NULL, len); + } +#endif } /*****************************************************************************/ @@ -455,7 +759,6 @@ ssl_gen_key_xrdp1(int key_size_in_bits, const char *exp, int exp_len, char *mod, int mod_len, char *pri, int pri_len) { BIGNUM *my_e; - RSA *my_key; char *lexp; char *lmod; char *lpri; @@ -477,12 +780,44 @@ ssl_gen_key_xrdp1(int key_size_in_bits, const char *exp, int exp_len, ssl_reverse_it(lexp, exp_len); my_e = BN_new(); BN_bin2bn((tui8 *)lexp, exp_len, my_e); - my_key = RSA_new(); + +#if OPENSSL_VERSION_NUMBER < 0x30000000L + const BIGNUM *n = NULL; + const BIGNUM *d = NULL; + RSA *my_key = RSA_new(); error = RSA_generate_key_ex(my_key, key_size_in_bits, my_e, 0) == 0; - const BIGNUM *n; - const BIGNUM *d; + /* After this call, n and d point directly into my_key, and are valid + * until my_key is free'd */ RSA_get0_key(my_key, &n, NULL, &d); +#else + BIGNUM *n = NULL; + BIGNUM *d = NULL; + OSSL_PARAM params[] = + { + OSSL_PARAM_construct_int("bits", &key_size_in_bits), + OSSL_PARAM_construct_end() + }; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + EVP_PKEY *pkey = NULL; + + if (pctx != NULL && + EVP_PKEY_keygen_init(pctx) > 0 && + EVP_PKEY_CTX_set_params(pctx, params) > 0 && + EVP_PKEY_generate(pctx, &pkey) > 0 && + EVP_PKEY_get_bn_param(pkey, "n", &n) > 0 && + EVP_PKEY_get_bn_param(pkey, "d", &d) > 0) + { + error = 0; + } + else + { + error = 1; + } + + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_free(pkey); +#endif if (error == 0) { @@ -517,7 +852,12 @@ ssl_gen_key_xrdp1(int key_size_in_bits, const char *exp, int exp_len, } BN_free(my_e); +#if OPENSSL_VERSION_NUMBER < 0x30000000L RSA_free(my_key); +#else + BN_free(n); + BN_clear_free(d); +#endif g_free(lexp); g_free(lmod); g_free(lpri); @@ -529,7 +869,11 @@ ssl_gen_key_xrdp1(int key_size_in_bits, const char *exp, int exp_len, see also * https://wiki.openssl.org/index.php/Diffie-Hellman_parameters * https://wiki.openssl.org/index.php/Manual:SSL_CTX_set_tmp_dh_callback(3) + * + * We dont do this for OpenSSL 3 - we use SSL_CTX_set_dh_auto() instead, as this + * can cater for different key sizes on the certificate */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L static DH *ssl_get_dh2236() { static unsigned char dh2236_p[] = @@ -591,6 +935,7 @@ static DH *ssl_get_dh2236() return dh; } +#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */ /*****************************************************************************/ struct ssl_tls * @@ -613,25 +958,6 @@ ssl_tls_create(struct trans *trans, const char *key, const char *cert) return self; } - -/*****************************************************************************/ -static void -dump_ssl_error_stack(struct ssl_tls *self) -{ - if (!self->error_logged) - { - /* Dump the error stack from the SSL library */ - unsigned long code; - char buff[256]; - while ((code = ERR_get_error()) != 0L) - { - ERR_error_string_n(code, buff, sizeof(buff)); - LOG(LOG_LEVEL_ERROR, "SSL: %s", buff); - } - self->error_logged = 1; - } -} - /*****************************************************************************/ static int ssl_tls_log_error(struct ssl_tls *self, const char *func, int value) @@ -752,6 +1078,7 @@ ssl_tls_accept(struct ssl_tls *self, long ssl_protocols, SSL_CTX_set_options(self->ctx, options); /* set DH parameters */ +#if OPENSSL_VERSION_NUMBER < 0x30000000L DH *dh = ssl_get_dh2236(); if (dh == NULL) { @@ -766,7 +1093,14 @@ ssl_tls_accept(struct ssl_tls *self, long ssl_protocols, return 1; } DH_free(dh); // ok to free, copied into ctx by SSL_CTX_set_tmp_dh() - +#else + if (!SSL_CTX_set_dh_auto(self->ctx, 1)) + { + LOG(LOG_LEVEL_ERROR, "TLS DHE auto failed to be enabled"); + dump_ssl_error_stack(self); + return 1; + } +#endif #if defined(SSL_CTX_set_ecdh_auto) if (!SSL_CTX_set_ecdh_auto(self->ctx, 1)) { -- cgit v1.2.3 From c894ba5b40011c36afdfb3d57547d2801fdf6409 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Fri, 28 Jan 2022 12:17:38 +0000 Subject: Better logging of classic connection security --- libxrdp/xrdp_rdp.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libxrdp/xrdp_rdp.c b/libxrdp/xrdp_rdp.c index c2b23289..d08f068a 100644 --- a/libxrdp/xrdp_rdp.c +++ b/libxrdp/xrdp_rdp.c @@ -942,11 +942,21 @@ xrdp_rdp_incoming(struct xrdp_rdp *self) /* log non-TLS connections */ else { + int crypt_level = self->sec_layer->crypt_level; + const char *security_level = + (crypt_level == CRYPT_LEVEL_NONE) ? "none" : + (crypt_level == CRYPT_LEVEL_LOW) ? "low" : + (crypt_level == CRYPT_LEVEL_CLIENT_COMPATIBLE) ? "medium" : + (crypt_level == CRYPT_LEVEL_HIGH) ? "high" : + (crypt_level == CRYPT_LEVEL_FIPS) ? "fips" : + /* default */ "unknown"; + LOG(LOG_LEVEL_INFO, "Non-TLS connection established from %s port %s: " - "encrypted with standard RDP security", + "with security level : %s", self->client_info.client_addr, - self->client_info.client_port); + self->client_info.client_port, + security_level); } return 0; -- cgit v1.2.3 From e79bc7f1815b25390b4228378845a1f9028e74fc Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 27 Jan 2022 15:55:53 +0000 Subject: Fix banner comments in test results --- tests/libxrdp/Makefile.am | 2 ++ tests/memtest/Makefile.am | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tests/libxrdp/Makefile.am b/tests/libxrdp/Makefile.am index e98e8d9c..30138cbf 100644 --- a/tests/libxrdp/Makefile.am +++ b/tests/libxrdp/Makefile.am @@ -6,6 +6,8 @@ AM_CPPFLAGS = \ LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/tap-driver.sh +PACKAGE_STRING = "libxrdp" + TESTS = test_libxrdp check_PROGRAMS = test_libxrdp diff --git a/tests/memtest/Makefile.am b/tests/memtest/Makefile.am index 40669125..8660fc99 100644 --- a/tests/memtest/Makefile.am +++ b/tests/memtest/Makefile.am @@ -2,6 +2,8 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/common +PACKAGE_STRING = "memtest" + check_PROGRAMS = \ memtest -- cgit v1.2.3 From 8b8cfbe1195bef6384674641225d40b5e98e88f4 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:31:53 +0000 Subject: Improve wrapping of openssl module --- common/ssl_calls.c | 26 ++++++++++++++++++++++---- common/ssl_calls.h | 22 ++++++++-------------- common/trans.c | 12 ++++-------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/common/ssl_calls.c b/common/ssl_calls.c index 792a9130..168add76 100644 --- a/common/ssl_calls.c +++ b/common/ssl_calls.c @@ -53,6 +53,17 @@ static EVP_CIPHER *g_cipher_des_ede3_cbc; /* DES3 CBC cipher */ static EVP_MAC *g_mac_hmac; /* HMAC MAC */ #endif +/* definition of ssl_tls */ +struct ssl_tls +{ + SSL *ssl; /* SSL * */ + SSL_CTX *ctx; /* SSL_CTX * */ + char *cert; + char *key; + struct trans *trans; + tintptr rwo; /* wait obj */ + int error_logged; /* Error has already been logged */ +}; #if OPENSSL_VERSION_NUMBER < 0x10100000L static inline HMAC_CTX * @@ -1392,16 +1403,23 @@ ssl_tls_can_recv(struct ssl_tls *tls, int sck, int millis) /*****************************************************************************/ const char * -ssl_get_version(const struct ssl_st *ssl) +ssl_get_version(const struct ssl_tls *ssl) { - return SSL_get_version(ssl); + return SSL_get_version(ssl->ssl); } /*****************************************************************************/ const char * -ssl_get_cipher_name(const struct ssl_st *ssl) +ssl_get_cipher_name(const struct ssl_tls *ssl) +{ + return SSL_get_cipher_name(ssl->ssl); +} + +/*****************************************************************************/ +tintptr +ssl_get_rwo(const struct ssl_tls *ssl) { - return SSL_get_cipher_name(ssl); + return ssl->rwo; } /*****************************************************************************/ diff --git a/common/ssl_calls.h b/common/ssl_calls.h index 78edd946..01b67406 100644 --- a/common/ssl_calls.h +++ b/common/ssl_calls.h @@ -22,6 +22,10 @@ #include "arch.h" +/* Incomplete types */ +struct ssl_tls; +struct trans; + int ssl_init(void); int @@ -81,18 +85,6 @@ int ssl_gen_key_xrdp1(int key_size_in_bits, const char *exp, int exp_len, char *mod, int mod_len, char *pri, int pri_len); -/* ssl_tls */ -struct ssl_tls -{ - struct ssl_st *ssl; /* SSL * */ - struct ssl_ctx_st *ctx; /* SSL_CTX * */ - char *cert; - char *key; - struct trans *trans; - tintptr rwo; /* wait obj */ - int error_logged; /* Error has already been logged */ -}; - /* xrdp_tls.c */ struct ssl_tls * ssl_tls_create(struct trans *trans, const char *key, const char *cert); @@ -110,12 +102,14 @@ ssl_tls_write(struct ssl_tls *tls, const char *data, int length); int ssl_tls_can_recv(struct ssl_tls *tls, int sck, int millis); const char * -ssl_get_version(const struct ssl_st *ssl); +ssl_get_version(const struct ssl_tls *ssl); const char * -ssl_get_cipher_name(const struct ssl_st *ssl); +ssl_get_cipher_name(const struct ssl_tls *ssl); int ssl_get_protocols_from_string(const char *str, long *ssl_protocols); const char * get_openssl_version(); +tintptr +ssl_get_rwo(const struct ssl_tls *ssl); #endif diff --git a/common/trans.c b/common/trans.c index 3afae55d..55d2a638 100644 --- a/common/trans.c +++ b/common/trans.c @@ -179,13 +179,9 @@ trans_get_wait_objs(struct trans *self, tbus *objs, int *count) objs[*count] = self->sck; (*count)++; - if (self->tls != 0) + if (self->tls != NULL && (objs[*count] = ssl_get_rwo(self->tls)) != 0) { - if (self->tls->rwo != 0) - { - objs[*count] = self->tls->rwo; - (*count)++; - } + (*count)++; } return 0; @@ -995,8 +991,8 @@ trans_set_tls_mode(struct trans *self, const char *key, const char *cert, self->trans_send = trans_tls_send; self->trans_can_recv = trans_tls_can_recv; - self->ssl_protocol = ssl_get_version(self->tls->ssl); - self->cipher_name = ssl_get_cipher_name(self->tls->ssl); + self->ssl_protocol = ssl_get_version(self->tls); + self->cipher_name = ssl_get_cipher_name(self->tls); return 0; } -- cgit v1.2.3 From 4699dced141698bde452abb0b3ec817503e11c27 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:23:51 +0000 Subject: Implement base64 without openssl --- common/base64.c | 246 ++++++++++++++++++++---- common/base64.h | 64 ++++++- tests/common/Makefile.am | 5 +- tests/common/test_base64.c | 400 ++++++++++++++++++++++++++++++++++++++++ tests/common/test_common.h | 4 + tests/common/test_common_main.c | 31 ++++ tests/common/test_ssl_calls.c | 26 --- xrdp/xrdp_login_wnd.c | 10 +- 8 files changed, 712 insertions(+), 74 deletions(-) create mode 100644 tests/common/test_base64.c diff --git a/common/base64.c b/common/base64.c index af01d7f5..0b8cfe67 100644 --- a/common/base64.c +++ b/common/base64.c @@ -1,5 +1,5 @@ /** - * Copyright (C) 2017 Koichiro Iwao, all xrdp contributors + * Copyright (C) 2022 Matt Burt, all xrdp contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,57 +26,229 @@ #include "string_calls.h" -#include -#include +#include "base64.h" -size_t -base64_decoded_bytes(const char *src) +/* + * Values for invalid and padding characters, used in the charmap + * for converting base64 to binary + * + * Thse value are specially chosen to make it easy to detect padding or + * invalid characters by or-ing together the values looked up in + * a base64 quantum */ +#define E_INVALID 0x40 +#define E_PAD 0x80 + +/* Determine the character set on this platform */ +#if ('a' == 0x61 && 'z' == 0x7a ) && \ + ('A' == 0x41 && 'Z' == 0x5a ) && \ + ('0' == 0x30 && '9' == 0x39 ) +# define PLATFORM_IS_ASCII 1 +#else +# error "Unrecognised character set on this platform" +#endif /* character set check */ + + +/* + * Define a table to map the base64 character values to bit values. + */ +#ifdef PLATFORM_IS_ASCII +#define CHARMAP_BASE 0x28 +#define E_IV E_INVALID /* For table alignment */ +const unsigned char charmap[] = +{ + /* 0x28 */ E_IV, E_IV, E_IV, 0x3e, E_IV, E_IV, E_IV, 0x3f, + /* 0x30 */ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + /* 0x38 */ 0x3c, 0x3d, E_IV, E_IV, E_IV, E_PAD, E_IV, E_IV, + /* 0x40 */ E_IV, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + /* 0x48 */ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + /* 0x50 */ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + /* 0x58 */ 0x17, 0x18, 0x19, E_IV, E_IV, E_IV, E_IV, E_IV, + /* 0x60 */ E_IV, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + /* 0x68 */ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + /* 0x70 */ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, + /* 0x78 */ 0x31, 0x32, 0x33 +}; +#undef E_IV +#endif /* PLATFORM_IS_ASCII */ + + +/** + * Lookup a value in the charmap + * + * @param x - byte to lookup. Only referenced once so can safely have + * side effects. + * @param dest - destination to assign result to. + */ +#define CM_LOOKUP(x,dest) \ + { \ + unsigned int t = (unsigned int)(x) - CHARMAP_BASE;\ + dest = (t < sizeof(charmap)) ? charmap[t] : E_INVALID; \ + } + +/*****************************************************************************/ + +int +base64_decode(const char *src, char *dst, size_t dst_len, size_t *actual_len) { - size_t len; - size_t padding; + *actual_len = 0; + size_t src_len; + size_t src_i = 0; + size_t dst_i = 0; + unsigned int a; /* Four characters of base64 quantum */ + unsigned int b; + unsigned int c; + unsigned int d; + unsigned int v; + +#define OUTPUT_CHAR(x) \ + { \ + if (dst_i < dst_len) \ + { \ + dst[dst_i] = (x);\ + } \ + ++dst_i; \ + } - len = g_strlen(src); - padding = 0; + src_len = g_strlen(src); - if (src[len - 1] == '=') + while (src_i < src_len) { - padding++; + if ((src_len - src_i) >= 4) + { + /* Usual case - full quantum */ + CM_LOOKUP(src[src_i++], a); + CM_LOOKUP(src[src_i++], b); + CM_LOOKUP(src[src_i++], c); + CM_LOOKUP(src[src_i++], d); + } + else + { + /* Add padding on the end to make up the full quantum */ + CM_LOOKUP(src[src_i++], a); + b = E_PAD; + c = E_PAD; + d = E_PAD; + if ((src_len - src_i) > 0) + { + CM_LOOKUP(src[src_i++], b); + } + if ((src_len - src_i) > 0) + { + CM_LOOKUP(src[src_i++], c); + } + } + + /* + * Bitwise-or the translated quantum values together, so that + * any invalid or padding characters can be detected with a + * single test */ + v = a | b | c | d; + + if ((v & E_INVALID) != 0) + { + return -1; /* At least one invalid character */ + } - if (src[len - 2] == '=') + if ((v & E_PAD) == 0) + { + /* No padding - a full quantum */ + v = (a << 18) | (b << 12) | (c << 6) | d; + OUTPUT_CHAR(v >> 16); + OUTPUT_CHAR((v >> 8) & 0xff); + OUTPUT_CHAR(v & 0xff); + } + else if (((a | b | c) & E_PAD) == 0) + { + /* No padding in the first 3 chars, so the padding must + * be at the end */ + v = (a << 10) | (b << 4) | (c >> 2); + OUTPUT_CHAR(v >> 8); + OUTPUT_CHAR(v & 0xff); + } + else if (((a | b) & E_PAD) == 0 && c == d) { - padding++; + /* No padding in first two chars, so if the last two chars are + * equal, they must both be padding */ + v = (a << 2) | (b >> 4); + OUTPUT_CHAR(v); + } + else + { + /* Illegal padding */ + return -1; } } - return len * 3 / 4 - padding; + *actual_len = dst_i; + return 0; + +#undef OUTPUT_CHAR } /*****************************************************************************/ -char * -base64_decode(char *dst, const char *src, size_t len) +size_t +base64_encode(const char *src, size_t src_len, char *dst, size_t dst_len) { - BIO *b64; - BIO *bio; - char *b64str; - size_t estimated_decoded_bytes; - size_t decoded_bytes; - - b64str = g_strdup(src); - estimated_decoded_bytes = base64_decoded_bytes(b64str); - dst[estimated_decoded_bytes] = '\0'; - - b64 = BIO_new(BIO_f_base64()); - bio = BIO_new_mem_buf(b64str, len); - bio = BIO_push(b64, bio); - BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); - decoded_bytes = BIO_read(bio, dst, len); - BIO_free_all(bio); - - /* if input is corrupt, return empty string */ - if (estimated_decoded_bytes != decoded_bytes) + char *p = dst; + size_t src_i = 0; + size_t max_src_len; + unsigned int v; + static const char *b64chr = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/="; + + /* Each three octets of the source results in four bytes at the output, + * plus we need a terminator. So we can work out the maximum number of + * source octets we can process */ + if (dst_len == 0) + { + max_src_len = 0; + } + else + { + max_src_len = (dst_len - 1) / 4 * 3; + } + + if (src_len > max_src_len) + { + src_len = max_src_len; + } + + while (src_i < src_len) { - g_strncpy(dst, "", sizeof("")); + switch (src_len - src_i) + { + case 1: + v = (unsigned int)(unsigned char)src[src_i++] << 4; + *p++ = b64chr[v >> 6]; + *p++ = b64chr[v & 0x3f]; + *p++ = b64chr[64]; + *p++ = b64chr[64]; + break; + + case 2: + v = (unsigned int)(unsigned char)src[src_i++] << 10; + v |= (unsigned int)(unsigned char)src[src_i++] << 2; + *p++ = b64chr[v >> 12]; + *p++ = b64chr[(v >> 6) & 0x3f]; + *p++ = b64chr[v & 0x3f]; + *p++ = b64chr[64]; + break; + + default: + v = (unsigned int)(unsigned char)src[src_i++] << 16; + v |= (unsigned int)(unsigned char)src[src_i++] << 8; + v |= (unsigned int)(unsigned char)src[src_i++]; + *p++ = b64chr[v >> 18]; + *p++ = b64chr[(v >> 12) & 0x3f]; + *p++ = b64chr[(v >> 6) & 0x3f]; + *p++ = b64chr[v & 0x3f]; + break; + } } - return dst; + *p = '\0'; + + return src_len; } diff --git a/common/base64.h b/common/base64.h index b53ed88f..1b341e9b 100644 --- a/common/base64.h +++ b/common/base64.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2017 Koichiro Iwao, all xrdp contributors + * Copyright (C) 2021 Koichiro Iwao, all xrdp contributors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,16 +17,66 @@ /** * @file common/base64.h * @brief Base64 encoder / decoder + * + * Base-64 is described in RFC4648. The following notes apply to this + * implementation:- + * - The only supported characters are [A-Za-z0-9+/=]. At present, + * embedded linefeeds and URL encodings are not supported. + * */ -#if !defined(SSL_CALLS_H) -#define SSL_CALLS_H +#if !defined(BASE64_CALLS_H) +#define BASE64_CALLS_H #include "arch.h" +/* + * Decodes a base64 string + * + * @param src Pointer to null-terminated source + * @param dst Pointer to output buffer + * @param dst_len Length of above. No more than this is written to the + * output buffer + * @param actual_len Pointer to value to receive actual length of decoded data + * @return 0 for success, 1 for an invalid input string + * + * The following notes apply to this implementation:- + * - Embedded padding is supported, provided it only occurs at the end + * of a quantum as described in RFC4648(4). This allows concatenated + * encodings to be fed into the decoder. + * - Padding of the last quantum is assumed if not provided. + * - Excess padding of the last quantum is ignored. + * + * Only dst_len bytes at most are written to the output. The length + * returned in actual_len however represents how much buffer is needed for + * a correct result. This may be more than dst_len, and enables the caller + * to detect a potential buffer overflow + */ +int +base64_decode(const char *src, char *dst, size_t dst_len, size_t *actual_len); + +/* + * Encodes a buffer as base64 + * + * @param src Pointer to source + * @param src_len Length of above. + * @param dst Pointer to output buffer for null-terminated string + * @param dst_len Length of above. No more than this is written to the + * output buffer + * @return Number of source characters processed + * + * The following notes apply to this implementation:- + * - Padding of the last quantum is always written if the number of + * source bytes is not divisible by 3. + * + * The number of source characters processed is always returned. If + * the destination buffer is too small for all the output plus a + * terminator, the returned value from this procedure will be less than + * src_len. In this case no padding characters will have been written, + * and the remaining characters can be converted with more calls to + * this procedure. + */ size_t -base64_decoded_bytes(const char *src); -char * -base64_decode(char *dst, const char *src, size_t len); +base64_encode(const char *src, size_t src_len, char *dst, size_t dst_len); -#endif +#endif /* BASE64_CALLS_H */ diff --git a/tests/common/Makefile.am b/tests/common/Makefile.am index 88548b71..e6686e31 100644 --- a/tests/common/Makefile.am +++ b/tests/common/Makefile.am @@ -6,6 +6,8 @@ AM_CPPFLAGS = \ LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ $(top_srcdir)/tap-driver.sh +PACKAGE_STRING = "libcommon" + TESTS = test_common check_PROGRAMS = test_common @@ -14,7 +16,8 @@ test_common_SOURCES = \ test_common_main.c \ test_string_calls.c \ test_os_calls.c \ - test_ssl_calls.c + test_ssl_calls.c \ + test_base64.c test_common_CFLAGS = \ @CHECK_CFLAGS@ \ diff --git a/tests/common/test_base64.c b/tests/common/test_base64.c new file mode 100644 index 00000000..4a70c921 --- /dev/null +++ b/tests/common/test_base64.c @@ -0,0 +1,400 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include "os_calls.h" +#include "string_calls.h" +#include "base64.h" + +#include "test_common.h" +/* +* These are the example test strings in RFC4648(10) +*/ +static const char *rfc4648_ex1_text = ""; +static const char *rfc4648_ex1_b64 = ""; +static const char *rfc4648_ex2_text = "f"; +static const char *rfc4648_ex2_b64 = "Zg=="; +static const char *rfc4648_ex3_text = "fo"; +static const char *rfc4648_ex3_b64 = "Zm8="; +static const char *rfc4648_ex4_text = "foo"; +static const char *rfc4648_ex4_b64 = "Zm9v"; +static const char *rfc4648_ex5_text = "foob"; +static const char *rfc4648_ex5_b64 = "Zm9vYg=="; +static const char *rfc4648_ex6_text = "fooba"; +static const char *rfc4648_ex6_b64 = "Zm9vYmE="; +static const char *rfc4648_ex7_text = "foobar"; +static const char *rfc4648_ex7_b64 = "Zm9vYmFy"; + +/* Every single valid base64 character, except padding */ +static const char *all_b64 = + "ABCDEFGHIJKL" + "MNOPQRSTUVWX" + "YZabcdefghij" + "klmnopqrstuv" + "wxyz01234567" + "89+/"; + +/* What we should get as binary if we decode this */ +static const char all_b64_decoded[] = +{ + '\x00', '\x10', '\x83', '\x10', '\x51', '\x87', '\x20', '\x92', '\x8b', + '\x30', '\xd3', '\x8f', '\x41', '\x14', '\x93', '\x51', '\x55', '\x97', + '\x61', '\x96', '\x9b', '\x71', '\xd7', '\x9f', '\x82', '\x18', '\xa3', + '\x92', '\x59', '\xa7', '\xa2', '\x9a', '\xab', '\xb2', '\xdb', '\xaf', + '\xc3', '\x1c', '\xb3', '\xd3', '\x5d', '\xb7', '\xe3', '\x9e', '\xbb', + '\xf3', '\xdf', '\xbf' +}; + +static void +test_rfc4648_to_b64(const char *plaintext, size_t len, const char *b64) +{ + char buff[256]; + size_t result; + + result = base64_encode(plaintext, len, buff, sizeof(buff)); + ck_assert_int_eq(result, len); + ck_assert_str_eq(buff, b64); + +} + +/* Text-only encoder wrapper */ +static void +test_rfc4648_to_b64_text(const char *plaintext, const char *b64) +{ + test_rfc4648_to_b64(plaintext, g_strlen(plaintext), b64); +} + +/* Text-only decoder wrapper for valid base64 */ +static void +test_rfc4648_from_b64_text(const char *b64, const char *text) +{ + char buff[256]; + size_t actual_len; + int result; + + result = base64_decode(b64, buff, sizeof(buff), &actual_len); + ck_assert_int_eq(result, 0); + ck_assert_int_lt(actual_len, sizeof(buff)); + buff[actual_len] = '\0'; + ck_assert_str_eq(buff, text); +} + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex1_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex1_text, rfc4648_ex1_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex1_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex1_b64, rfc4648_ex1_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex2_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex2_text, rfc4648_ex2_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex2_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex2_b64, rfc4648_ex2_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex3_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex3_text, rfc4648_ex3_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex3_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex3_b64, rfc4648_ex3_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex4_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex4_text, rfc4648_ex4_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex4_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex4_b64, rfc4648_ex4_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex5_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex5_text, rfc4648_ex5_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex5_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex5_b64, rfc4648_ex5_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex6_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex6_text, rfc4648_ex6_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex6_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex6_b64, rfc4648_ex6_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex7_to) +{ + test_rfc4648_to_b64_text(rfc4648_ex7_text, rfc4648_ex7_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_rfc4648_ex7_from) +{ + test_rfc4648_from_b64_text(rfc4648_ex7_b64, rfc4648_ex7_text); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_all_valid_from) +{ + char buff[256]; + size_t actual_len; + int result; + char *str_result; + char *str_expected; + + result = base64_decode(all_b64, buff, sizeof(buff), &actual_len); + ck_assert_int_eq(result, 0); + ck_assert_int_eq(actual_len, sizeof(all_b64_decoded)); + str_result = bin_to_hex(buff, actual_len); + str_expected = bin_to_hex(all_b64_decoded, sizeof(all_b64_decoded)); + ck_assert_str_eq(str_result, str_expected); + free(str_result); + free(str_expected); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_all_valid_to) +{ + char buff[256]; + size_t result; + + result = base64_encode(all_b64_decoded, sizeof(all_b64_decoded), + buff, sizeof(buff)); + ck_assert_int_eq(result, sizeof(all_b64_decoded)); + ck_assert_str_eq(buff, all_b64); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_all_invalid) +{ + char buff[256]; + size_t actual_len; + char valid[256] = {0}; + char encoded[5] = { "T0sh" }; /* Decodes to 'OK!' */ + int result; + + /* Make a note of all the valid b64 characters */ + unsigned int i = 0; + unsigned char c; + while ((c = all_b64[i]) != '\0') + { + valid[c] = 1; + ++i; + } + + /* Check the decoder's working on a simple string...*/ + result = base64_decode(encoded, buff, sizeof(buff), &actual_len); + ck_assert_int_eq(result, 0); + ck_assert_int_eq(actual_len, 3); + buff[actual_len] = '\0'; + ck_assert_str_eq(buff, "OK!"); + + /* Now replace the 1st character with all invalid characters in turn, + * and check they're rejected */ + for (i = 0 ; i < 256; ++i) + { + if (i != '\0' && !valid[i]) /* Don't try the string terminator char! */ + { + encoded[0] = i; + result = base64_decode(encoded, buff, sizeof(buff), &actual_len); + if (result == 0) + { + ck_abort_msg("Character 0x%02x was not rejected", i); + } + } + } +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_small_buffer_encode) +{ + char buff[10 * 4 + 1]; /* Enough space for 10 quanta */ + + size_t result; + + result = base64_encode(all_b64_decoded, sizeof(all_b64_decoded), + buff, sizeof(buff)); + /* Should have read 10 lots of 24 bits from the input */ + ck_assert_int_eq(result, 10 * 3); + ck_assert_str_eq(buff, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn"); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_small_buffer_decode) +{ + char buff[10]; /* Enough space for 10 chars */ + + size_t actual_len; + int result; + char *str_result; + char *str_expected; + + result = base64_decode(all_b64, buff, sizeof(buff), &actual_len); + ck_assert_int_eq(result, 0); + ck_assert_int_eq(actual_len, sizeof(all_b64_decoded)); + str_result = bin_to_hex(buff, sizeof(buff)); + str_expected = bin_to_hex(all_b64_decoded, sizeof(buff)); + ck_assert_str_eq(str_result, str_expected); + free(str_result); + free(str_expected); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_add_pad_one) +{ + /* Check that a missing trailing '=' is added when decoding */ + test_rfc4648_from_b64_text("Zm8", "fo"); /* RFC4648 example 3 */ +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_add_pad_two) +{ + /* Check that two missing trailing '=' chars are added when decoding */ + test_rfc4648_from_b64_text("Zg", "f"); /* RFC4648 example 2 */ +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_bad_pad) +{ + char buff[16]; + size_t actual_len; + + /* Check all bad quanta with padding chars */ + static const char *bad_pad[] = + { + "=AAA", + "A=AA", + "AA=A", + "==AA", + "=A=A", + "=AA=", + "A==A", + "A=A=", + "===A", + "A===", + NULL + }; + const char **p; + + for (p = bad_pad ; *p != NULL ; ++p) + { + int result = base64_decode(*p, buff, sizeof(buff), &actual_len); + if (result == 0) + { + ck_abort_msg("Padding '%s' was not rejected", *p); + } + } +} +END_TEST + +/******************************************************************************/ +START_TEST(test_b64_concat_pad) +{ + const char *src = + "VGVzdA==" /* Test */ + "IA==" /* */ + "Y29uY2F0ZW5hdGVk" /* concatenated */ + "IA==" /* */ + "cGFkZGluZw=="; /*padding */ + const char *expected = "Test concatenated padding"; + char buff[64]; + size_t actual_len; + int result; + + result = base64_decode(src, buff, sizeof(buff), &actual_len); + ck_assert_int_eq(result, 0); + ck_assert_int_eq(actual_len, g_strlen(expected)); + buff[actual_len] = '\0'; + ck_assert_str_eq(buff, expected); +} +END_TEST + +/******************************************************************************/ +Suite * +make_suite_test_base64(void) +{ + Suite *s; + TCase *tc_b64; + + s = suite_create("base64"); + + tc_b64 = tcase_create("base64"); + suite_add_tcase(s, tc_b64); + tcase_add_test(tc_b64, test_b64_rfc4648_ex1_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex1_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex2_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex2_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex3_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex3_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex4_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex4_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex5_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex5_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex6_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex6_from); + tcase_add_test(tc_b64, test_b64_rfc4648_ex7_to); + tcase_add_test(tc_b64, test_b64_rfc4648_ex7_from); + tcase_add_test(tc_b64, test_b64_all_valid_from); + tcase_add_test(tc_b64, test_b64_all_valid_to); + tcase_add_test(tc_b64, test_b64_all_invalid); + tcase_add_test(tc_b64, test_b64_small_buffer_encode); + tcase_add_test(tc_b64, test_b64_small_buffer_decode); + tcase_add_test(tc_b64, test_b64_add_pad_one); + tcase_add_test(tc_b64, test_b64_add_pad_two); + tcase_add_test(tc_b64, test_b64_bad_pad); + tcase_add_test(tc_b64, test_b64_concat_pad); + + return s; +} diff --git a/tests/common/test_common.h b/tests/common/test_common.h index 3493c3bf..ed422ab4 100644 --- a/tests/common/test_common.h +++ b/tests/common/test_common.h @@ -4,8 +4,12 @@ #include +char * +bin_to_hex(const char *input, int length); + Suite *make_suite_test_string(void); Suite *make_suite_test_os_calls(void); Suite *make_suite_test_ssl_calls(void); +Suite *make_suite_test_base64(void); #endif /* TEST_COMMON_H */ diff --git a/tests/common/test_common_main.c b/tests/common/test_common_main.c index 2674bea0..a08bc7f7 100644 --- a/tests/common/test_common_main.c +++ b/tests/common/test_common_main.c @@ -10,6 +10,36 @@ #include "test_common.h" +/** + * Converts a binary buffer to a hexadecimal representation + * + * Result must be free'd after use + */ +char * +bin_to_hex(const char *input, int length) +{ + int i; + char *result = (char *)malloc(length * 2 + 1); + if (result != NULL) + { + char *p = result; + const char *hexdigit = "0123456789abcdef"; + for (i = 0 ; i < length ; ++i) + { + int c = input[i]; + if (c < 0) + { + c += 256; + } + *p++ = hexdigit[ c / 16]; + *p++ = hexdigit[ c % 16]; + } + *p = '\0'; + } + + return result; +} + int main (void) { int number_failed; @@ -18,6 +48,7 @@ int main (void) sr = srunner_create (make_suite_test_string()); srunner_add_suite(sr, make_suite_test_os_calls()); srunner_add_suite(sr, make_suite_test_ssl_calls()); + srunner_add_suite(sr, make_suite_test_base64()); // srunner_add_suite(sr, make_list_suite()); srunner_set_tap(sr, "-"); diff --git a/tests/common/test_ssl_calls.c b/tests/common/test_ssl_calls.c index c5198fd9..56d8935b 100644 --- a/tests/common/test_ssl_calls.c +++ b/tests/common/test_ssl_calls.c @@ -9,32 +9,6 @@ #include "test_common.h" -static -char *bin_to_hex(const char *input, int length) -{ - int i; - char *result = (char *)g_malloc(length * 2 + 1, 0); - if (result != NULL) - { - char *p = result; - const char *hexdigit = "0123456789abcdef"; - for (i = 0 ; i < length ; ++i) - { - int c = input[i]; - if (c < 0) - { - c += 256; - } - *p++ = hexdigit[ c / 16]; - *p++ = hexdigit[ c % 16]; - } - *p = '\0'; - } - - return result; -} - - START_TEST(test_rc4_enc_ok) { const char *key = "16_byte_key-----"; diff --git a/xrdp/xrdp_login_wnd.c b/xrdp/xrdp_login_wnd.c index a1d7e9a5..da5917b2 100644 --- a/xrdp/xrdp_login_wnd.c +++ b/xrdp/xrdp_login_wnd.c @@ -346,6 +346,7 @@ xrdp_wm_show_edits(struct xrdp_wm *self, struct xrdp_bitmap *combo) struct xrdp_cfg_globals *globals; char resultIP[256]; char *plain; /* base64 decoded string */ + size_t plain_length; /* length of decoded base64 string */ size_t base64_length; /* length of base64 string */ globals = &self->xrdp_config->cfg_globals; @@ -379,8 +380,9 @@ xrdp_wm_show_edits(struct xrdp_wm *self, struct xrdp_bitmap *combo) { base64_length = g_strlen(value + BASE64PREFIX_LEN); plain = (char *)g_malloc(base64_length, 0); - base64_decode(plain, value + BASE64PREFIX_LEN, base64_length); - g_strncpy(value, plain, g_strlen(plain)); + base64_decode(value + BASE64PREFIX_LEN, + plain, base64_length, &plain_length); + g_strncpy(value, plain, plain_length); g_free(plain); } else if (g_strncmp(ASK, value, ASK_LEN) == 0) @@ -421,7 +423,9 @@ xrdp_wm_show_edits(struct xrdp_wm *self, struct xrdp_bitmap *combo) { base64_length = g_strlen(value + ASK_LEN + BASE64PREFIX_LEN); plain = (char *)g_malloc(base64_length, 0); - base64_decode(plain, value + ASK_LEN + BASE64PREFIX_LEN, base64_length); + base64_decode(value + ASK_LEN + BASE64PREFIX_LEN, + plain, base64_length, &plain_length); + plain[plain_length] = '\0'; g_strncpy(b->caption1, plain, 255); g_free(plain); } -- cgit v1.2.3 From eb4a8e342dcf848ca716792763ca1904e913ca9d Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 2 Feb 2022 10:39:50 +0000 Subject: Add lower bound to sesman data input size check --- sesman/sesman.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sesman/sesman.c b/sesman/sesman.c index a8576905..e2b057e6 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -276,6 +276,7 @@ sesman_close_all(void) static int sesman_data_in(struct trans *self) { +#define HEADER_SIZE 8 int version; int size; @@ -283,9 +284,9 @@ sesman_data_in(struct trans *self) { in_uint32_be(self->in_s, version); in_uint32_be(self->in_s, size); - if (size > self->in_s->size) + if (size < HEADER_SIZE || size > self->in_s->size) { - LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size"); + LOG(LOG_LEVEL_ERROR, "sesman_data_in: bad message size %d", size); return 1; } self->header_size = size; @@ -302,11 +303,12 @@ sesman_data_in(struct trans *self) return 1; } /* reset for next message */ - self->header_size = 8; + self->header_size = HEADER_SIZE; self->extra_flags = 0; init_stream(self->in_s, 0); /* Reset input stream pointers */ } return 0; +#undef HEADER_SIZE } /******************************************************************************/ -- cgit v1.2.3 From 79bc7c040e6024ce58be741cea40176961b07d12 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 7 Feb 2022 15:54:38 +0900 Subject: Fix typo in past CVE number --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 8c959801..d0e60959 100644 --- a/NEWS.md +++ b/NEWS.md @@ -208,7 +208,7 @@ These changes are likely to impact operating system package builders and those b 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) +* [CVE-2020-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. -- cgit v1.2.3 From baf62457a3a75be4dc56c25778f02e49ae96b32a Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Mon, 7 Feb 2022 09:31:34 +0000 Subject: Move to cppcheck 2.7 and bump default threads to 2 --- .github/workflows/build.yml | 2 +- scripts/run_cppcheck.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d91f1dff..0b02237d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,7 @@ jobs: CC: gcc # This is required to use a version of cppcheck other than that # supplied with the operating system - CPPCHECK_VER: 2.6 + CPPCHECK_VER: 2.7 CPPCHECK_REPO: https://github.com/danmar/cppcheck.git steps: # This is currently the only way to get a version into diff --git a/scripts/run_cppcheck.sh b/scripts/run_cppcheck.sh index 2c152782..65ec254e 100755 --- a/scripts/run_cppcheck.sh +++ b/scripts/run_cppcheck.sh @@ -48,7 +48,7 @@ fi # Any options/directories specified? if [ $# -eq 0 ]; then - set -- . + set -- -j 2 . fi # Display the cppcheck version and command for debugging -- cgit v1.2.3 From e6c098e750ec232a87a55b2cc1120fe5e1ee2312 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 9 Feb 2022 10:18:15 +0000 Subject: Remove s_check() macro --- common/parse.h | 3 --- sesman/chansrv/clipboard.c | 2 +- vnc/vnc_clip.c | 2 +- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/common/parse.h b/common/parse.h index b27bb0c9..a3721ef8 100644 --- a/common/parse.h +++ b/common/parse.h @@ -89,9 +89,6 @@ parser_stream_overflow_check(const struct stream *s, int n, int is_out, # define S_CHECK_REM_OUT(s,n) #endif -/******************************************************************************/ -#define s_check(s) s_check_rem(s, 0) - /******************************************************************************/ #define s_check_rem(s, n) ((s)->p + (n) <= (s)->end) diff --git a/sesman/chansrv/clipboard.c b/sesman/chansrv/clipboard.c index e2a716ef..d5422483 100644 --- a/sesman/chansrv/clipboard.c +++ b/sesman/chansrv/clipboard.c @@ -1334,7 +1334,7 @@ clipboard_process_data_response(struct stream *s, int clip_msg_status, return 0; } index = 0; - while (s_check(s)) + while (s_check_rem(s, 2)) { in_uint16_le(s, wchr); wtext[index] = wchr; diff --git a/vnc/vnc_clip.c b/vnc/vnc_clip.c index ebbfae36..364bdff7 100644 --- a/vnc/vnc_clip.c +++ b/vnc/vnc_clip.c @@ -575,7 +575,7 @@ handle_cb_format_data_response(struct vnc *v, struct stream *s) { case CF_TEXT: lastc = '\0'; - while (s_check(s)) + while (s_check_rem(s, 1)) { in_uint8(s, c); if (c == '\n' && lastc == '\r') -- cgit v1.2.3 From 35d400a8990cfd08caaecc96a87d3686e2a7a166 Mon Sep 17 00:00:00 2001 From: zbstao Date: Thu, 10 Feb 2022 09:42:33 +0800 Subject: Fixed possible SIGCHILD signal lost When multiple(eg. 20) xrdp connections are disconnected at the same time(eg. close all rdp client at the same time), zombie process may be spawned. --- sesman/sig.c | 12 ++++++++---- xrdp/xrdp.c | 4 +--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/sesman/sig.c b/sesman/sig.c index 53fb8bd1..fabff3af 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -122,12 +122,16 @@ sig_sesman_session_end(int sig) return; } - pid = g_waitchild(); - - if (pid > 0) + do { - session_kill(pid); + pid = g_waitchild(); + + if (pid > 0) + { + session_kill(pid); + } } + while (pid >= 0); } /******************************************************************************/ diff --git a/xrdp/xrdp.c b/xrdp/xrdp.c index ecaa9983..6da93884 100644 --- a/xrdp/xrdp.c +++ b/xrdp/xrdp.c @@ -163,9 +163,7 @@ xrdp_shutdown(int sig) static void xrdp_child(int sig) { - int safety; - - for (safety = 0; (g_waitchild() >= 0) && (safety <= 10); safety++) + while (g_waitchild() >= 0) { } } -- cgit v1.2.3 From a0f4d94cfeb38655a3d49c388def1cfdd63680cc Mon Sep 17 00:00:00 2001 From: Nexarian Date: Mon, 14 Feb 2022 14:18:24 -0500 Subject: Fix NPEs in log.c Multiple NPEs can happen in the internal_log_config_copy procedure, and we need to address this before we merge in changes for egfx. --- common/log.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/common/log.c b/common/log.c index b2bab18e..bfce267a 100644 --- a/common/log.c +++ b/common/log.c @@ -495,6 +495,11 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) { int i; + if (src == NULL || dest == NULL) + { + return; + } + dest->enable_syslog = src->enable_syslog; dest->fd = src->fd; dest->log_file = g_strdup(src->log_file); @@ -508,6 +513,21 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) dest->console_level = src->console_level; dest->enable_pid = src->enable_pid; dest->dump_on_start = src->dump_on_start; + + if (src->per_logger_level == NULL) + { + return; + } + if (dest->per_logger_level == NULL) + { + dest->per_logger_level = list_create(); + if (dest->per_logger_level == NULL) + { + return; + } + dest->per_logger_level->auto_free = 1; + } + for (i = 0; i < src->per_logger_level->count; ++i) { struct log_logger_level *dst_logger = -- cgit v1.2.3 From 773a8f7da1660a2e728963d5717341541f87ec0d Mon Sep 17 00:00:00 2001 From: Nexarian Date: Mon, 14 Feb 2022 14:41:37 -0500 Subject: Move DRDYNVC_STATUS_* to xrdp_channel.h These statuses are necessary for egfx resizing, as visibility to channel status is a pre-req for closing and re-opening a channel. --- libxrdp/Makefile.am | 1 + libxrdp/xrdp_channel.c | 14 +------------- libxrdp/xrdp_channel.h | 42 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 13 deletions(-) create mode 100644 libxrdp/xrdp_channel.h diff --git a/libxrdp/Makefile.am b/libxrdp/Makefile.am index c843a531..49df6c65 100644 --- a/libxrdp/Makefile.am +++ b/libxrdp/Makefile.am @@ -45,6 +45,7 @@ libxrdp_la_SOURCES = \ xrdp_bitmap_compress.c \ xrdp_caps.c \ xrdp_channel.c \ + xrdp_channel.h \ xrdp_fastpath.c \ xrdp_iso.c \ xrdp_jpeg_compress.c \ diff --git a/libxrdp/xrdp_channel.c b/libxrdp/xrdp_channel.c index 16a8668e..fff00ece 100644 --- a/libxrdp/xrdp_channel.c +++ b/libxrdp/xrdp_channel.c @@ -24,6 +24,7 @@ #include "libxrdp.h" #include "string_calls.h" +#include "xrdp_channel.h" #define CMD_DVC_OPEN_CHANNEL 0x10 #define CMD_DVC_DATA_FIRST 0x20 @@ -31,19 +32,6 @@ #define CMD_DVC_CLOSE_CHANNEL 0x40 #define CMD_DVC_CAPABILITY 0x50 -#define XRDP_DRDYNVC_STATUS_CLOSED 0 -#define XRDP_DRDYNVC_STATUS_OPEN_SENT 1 -#define XRDP_DRDYNVC_STATUS_OPEN 2 -#define XRDP_DRDYNVC_STATUS_CLOSE_SENT 3 - -#define XRDP_DRDYNVC_STATUS_TO_STR(status) \ - ((status) == XRDP_DRDYNVC_STATUS_CLOSED ? "CLOSED" : \ - (status) == XRDP_DRDYNVC_STATUS_OPEN_SENT ? "OPEN_SENT" : \ - (status) == XRDP_DRDYNVC_STATUS_OPEN ? "OPEN" : \ - (status) == XRDP_DRDYNVC_STATUS_CLOSE_SENT ? "CLOSE_SENT" : \ - "unknown" \ - ) - #define XRDP_DRDYNVC_CHANNEL_ID_TO_NAME(self, chan_id) \ (xrdp_channel_get_item((self), (chan_id)) != NULL \ ? xrdp_channel_get_item((self), (chan_id))->name \ diff --git a/libxrdp/xrdp_channel.h b/libxrdp/xrdp_channel.h new file mode 100644 index 00000000..9f795911 --- /dev/null +++ b/libxrdp/xrdp_channel.h @@ -0,0 +1,42 @@ +/** + * xrdp: A Remote Desktop Protocol server. + * + * MS-RDPEDYC : Definitions related to documentation in [MS-RDPEDYC] + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * References to MS-RDPEDYC are currently correct for v20210406 of that + * document + */ + +#if !defined(XRDP_CHANNEL_H) +#define XRDP_CHANNEL_H + +/* + These are not directly defined in MS-RDPEDYC, but + they are derived statuses needed to implement it. +*/ +#define XRDP_DRDYNVC_STATUS_CLOSED 0 +#define XRDP_DRDYNVC_STATUS_OPEN_SENT 1 +#define XRDP_DRDYNVC_STATUS_OPEN 2 +#define XRDP_DRDYNVC_STATUS_CLOSE_SENT 3 + +#define XRDP_DRDYNVC_STATUS_TO_STR(status) \ + ((status) == XRDP_DRDYNVC_STATUS_CLOSED ? "CLOSED" : \ + (status) == XRDP_DRDYNVC_STATUS_OPEN_SENT ? "OPEN_SENT" : \ + (status) == XRDP_DRDYNVC_STATUS_OPEN ? "OPEN" : \ + (status) == XRDP_DRDYNVC_STATUS_CLOSE_SENT ? "CLOSE_SENT" : \ + "unknown" \ + ) + +#endif /* XRDP_CHANNEL_H */ -- cgit v1.2.3 From ff39ce719e966ecd2b104e1c3cc87a2f34f6a928 Mon Sep 17 00:00:00 2001 From: zbstao Date: Tue, 15 Feb 2022 09:41:21 +0800 Subject: Fixed possible infinite loop Fixed possible infinite loop --- sesman/sig.c | 2 +- xrdp/xrdp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sesman/sig.c b/sesman/sig.c index fabff3af..cc29dea1 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -131,7 +131,7 @@ sig_sesman_session_end(int sig) session_kill(pid); } } - while (pid >= 0); + while (pid > 0); } /******************************************************************************/ diff --git a/xrdp/xrdp.c b/xrdp/xrdp.c index 6da93884..22eee5b3 100644 --- a/xrdp/xrdp.c +++ b/xrdp/xrdp.c @@ -163,7 +163,7 @@ xrdp_shutdown(int sig) static void xrdp_child(int sig) { - while (g_waitchild() >= 0) + while (g_waitchild() > 0) { } } -- cgit v1.2.3 From d23f7328f8ba02eee04cba34ad984981ef0734e4 Mon Sep 17 00:00:00 2001 From: Nexarian Date: Mon, 14 Feb 2022 22:03:22 -0500 Subject: Minor logging fixes in xrdp_iso.c Two logging errors found while working in these files. --- libxrdp/xrdp_channel.c | 2 +- libxrdp/xrdp_iso.c | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libxrdp/xrdp_channel.c b/libxrdp/xrdp_channel.c index fff00ece..f43804d7 100644 --- a/libxrdp/xrdp_channel.c +++ b/libxrdp/xrdp_channel.c @@ -923,7 +923,7 @@ xrdp_channel_drdynvc_close(struct xrdp_channel *self, int chan_id) LOG_DEVEL(LOG_LEVEL_TRACE, "Sending [MS-RDPEDYC] DYNVC_CLOSE " "cbId %d, Sp 0, Cmd 0x%2.2x, ChannelId %d", - cbChId, CMD_DVC_OPEN_CHANNEL, ChId); + cbChId, CMD_DVC_CLOSE_CHANNEL, ChId); if (xrdp_channel_send(self, s, static_channel_id, total_data_len, static_flags) != 0) { diff --git a/libxrdp/xrdp_iso.c b/libxrdp/xrdp_iso.c index f2593694..dde7a3ea 100644 --- a/libxrdp/xrdp_iso.c +++ b/libxrdp/xrdp_iso.c @@ -428,7 +428,8 @@ xrdp_iso_send_cc(struct xrdp_iso *self) out_uint16_le(s, 8); /* length (must be 8) */ out_uint32_le(s, self->selectedProtocol); /* selectedProtocol */ LOG_DEVEL(LOG_LEVEL_TRACE, "Adding structure [MS-RDPBCGR] RDP_NEG_RSP " - "flags 0, length 8, selectedProtocol 0x%8.8x", + "flags 0x%02x, length 8, selectedProtocol 0x%8.8x", + EXTENDED_CLIENT_DATA_SUPPORTED, self->selectedProtocol); } } -- cgit v1.2.3 From 1309ea405e7dc538d18025a275c43dac66947ec5 Mon Sep 17 00:00:00 2001 From: zbstao Date: Tue, 15 Feb 2022 22:32:46 +0800 Subject: Fixed g_waitpid function Fixed g_waitpid function --- common/os_calls.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/common/os_calls.c b/common/os_calls.c index dd2e964d..e7dea20e 100644 --- a/common/os_calls.c +++ b/common/os_calls.c @@ -2973,7 +2973,7 @@ g_waitchild(void) /*****************************************************************************/ /* does not work in win32 - returns pid of process that exits or zero if signal occurred */ + returns pid of process that exits or <= 0 if no process was found */ int g_waitpid(int pid) { @@ -2989,14 +2989,6 @@ g_waitpid(int pid) else { rv = waitpid(pid, 0, 0); - - if (rv == -1) - { - if (errno == EINTR) /* signal occurred */ - { - rv = 0; - } - } } return rv; -- cgit v1.2.3 From b689707d15f8da4d714e969f3cac7eb340115875 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Wed, 16 Feb 2022 11:59:56 +0000 Subject: Remove unnecessary log message --- sesman/chansrv/chansrv_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sesman/chansrv/chansrv_config.c b/sesman/chansrv/chansrv_config.c index 64297bcd..e3c30721 100644 --- a/sesman/chansrv/chansrv_config.c +++ b/sesman/chansrv/chansrv_config.c @@ -341,7 +341,7 @@ config_dump(struct config_chansrv *config) clip_restrict_map, ',', buf, sizeof(buf)); g_writeln(" RestrictOutboundClipboard: %s", buf); } - g_writeln(" RestrictInboundClipboard: %s", buf); + if (config->restrict_inbound_clipboard == CLIP_RESTRICT_NONE) { g_writeln(" RestrictInboundClipboard: %s", "none"); -- cgit v1.2.3 From fcd991844a11a295ef7b5dce6c6bba9c00b44cba Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 3 Mar 2022 15:35:13 +0000 Subject: sesman : Move global declarations to sesman.h --- sesman/access.c | 2 -- sesman/env.c | 3 --- sesman/scp.c | 2 -- sesman/scp_v0.c | 2 -- sesman/scp_v1.c | 2 -- sesman/scp_v1_mng.c | 2 -- sesman/sesman.h | 7 +++++++ sesman/session.c | 2 -- sesman/sig.c | 4 ---- sesman/verify_user.c | 2 -- sesman/verify_user_bsd.c | 2 -- 11 files changed, 7 insertions(+), 23 deletions(-) diff --git a/sesman/access.c b/sesman/access.c index 4b8489aa..442d9e7b 100644 --- a/sesman/access.c +++ b/sesman/access.c @@ -31,8 +31,6 @@ #include "sesman.h" #include "string_calls.h" -extern struct config_sesman *g_cfg; /* in sesman.c */ - /******************************************************************************/ int access_login_allowed(const char *user) diff --git a/sesman/env.c b/sesman/env.c index b8b2c030..f3b3dc63 100644 --- a/sesman/env.c +++ b/sesman/env.c @@ -36,9 +36,6 @@ #include "ssl_calls.h" #include "string_calls.h" -extern unsigned char g_fixedkey[8]; /* in sesman.c */ -extern struct config_sesman *g_cfg; /* in sesman.c */ - /******************************************************************************/ int env_check_password_file(const char *filename, const char *passwd) diff --git a/sesman/scp.c b/sesman/scp.c index 36cf4045..62f6af39 100644 --- a/sesman/scp.c +++ b/sesman/scp.c @@ -33,8 +33,6 @@ #include "sesman.h" -extern struct config_sesman *g_cfg; /* in sesman.c */ - /******************************************************************************/ enum SCP_SERVER_STATES_E scp_process(struct trans *t, struct SCP_SESSION *sdata) diff --git a/sesman/scp_v0.c b/sesman/scp_v0.c index 100c5902..195169fa 100644 --- a/sesman/scp_v0.c +++ b/sesman/scp_v0.c @@ -30,8 +30,6 @@ #include "sesman.h" -extern struct config_sesman *g_cfg; /* in sesman.c */ - /******************************************************************************/ enum SCP_SERVER_STATES_E scp_v0_process(struct trans *t, struct SCP_SESSION *s) diff --git a/sesman/scp_v1.c b/sesman/scp_v1.c index a8a59ebd..91a47e07 100644 --- a/sesman/scp_v1.c +++ b/sesman/scp_v1.c @@ -33,8 +33,6 @@ //#include "libscp_types.h" #include "libscp.h" -extern struct config_sesman *g_cfg; /* in sesman.c */ - static void parseCommonStates(enum SCP_SERVER_STATES_E e, const char *f); diff --git a/sesman/scp_v1_mng.c b/sesman/scp_v1_mng.c index 1ae13476..2b7ff216 100644 --- a/sesman/scp_v1_mng.c +++ b/sesman/scp_v1_mng.c @@ -32,8 +32,6 @@ #include "libscp.h" -extern struct config_sesman *g_cfg; /* in sesman.c */ - static void parseCommonStates(enum SCP_SERVER_STATES_E e, const char *f); /******************************************************************************/ diff --git a/sesman/sesman.h b/sesman/sesman.h index ebffff47..0c0a4c6d 100644 --- a/sesman/sesman.h +++ b/sesman/sesman.h @@ -41,6 +41,13 @@ #include "libscp.h" +/* Globals */ +extern struct config_sesman *g_cfg; +extern unsigned char g_fixedkey[8]; +extern tintptr g_term_event; +extern int g_pid; + + /* * Close all file descriptors used by sesman. * diff --git a/sesman/session.c b/sesman/session.c index 28fdb3d9..10b8a16d 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -47,8 +47,6 @@ #define PR_SET_NO_NEW_PRIVS 38 #endif -extern unsigned char g_fixedkey[8]; -extern struct config_sesman *g_cfg; /* in sesman.c */ struct session_chain *g_sessions; int g_session_count; diff --git a/sesman/sig.c b/sesman/sig.c index cc29dea1..0c4c4483 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -32,10 +32,6 @@ #include "sesman.h" -extern int g_pid; -extern struct config_sesman *g_cfg; /* in sesman.c */ -extern tbus g_term_event; - /******************************************************************************/ void sig_sesman_shutdown(int sig) diff --git a/sesman/verify_user.c b/sesman/verify_user.c index 76918d68..2c72d38e 100644 --- a/sesman/verify_user.c +++ b/sesman/verify_user.c @@ -42,8 +42,6 @@ #define SECS_PER_DAY (24L*3600L) #endif -extern struct config_sesman *g_cfg; /* in sesman.c */ - static int auth_crypt_pwd(const char *pwd, const char *pln, char *crp); diff --git a/sesman/verify_user_bsd.c b/sesman/verify_user_bsd.c index 4b7793d1..06489653 100644 --- a/sesman/verify_user_bsd.c +++ b/sesman/verify_user_bsd.c @@ -43,8 +43,6 @@ #define SECS_PER_DAY (24L*3600L) #endif -extern struct config_sesman *g_cfg; /* in sesman.c */ - /******************************************************************************/ /* returns boolean */ long -- cgit v1.2.3 From a94ddce0bd9903146f98dac4803807f02a22b460 Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 3 Mar 2022 16:26:59 +0000 Subject: logging : Remove processing for unused variables --- common/log.c | 14 ++++++++++++-- common/log.h | 6 ++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/common/log.c b/common/log.c index bfce267a..8273e25b 100644 --- a/common/log.c +++ b/common/log.c @@ -470,6 +470,7 @@ internalInitAndAllocStruct(void) { ret->fd = -1; ret->enable_syslog = 0; +#ifdef LOG_PER_LOGGER_LEVEL ret->per_logger_level = list_create(); if (ret->per_logger_level != NULL) { @@ -481,6 +482,7 @@ internalInitAndAllocStruct(void) g_free(ret); ret = NULL; } +#endif } else { @@ -493,8 +495,6 @@ internalInitAndAllocStruct(void) void internal_log_config_copy(struct log_config *dest, const struct log_config *src) { - int i; - if (src == NULL || dest == NULL) { return; @@ -504,8 +504,10 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) dest->fd = src->fd; dest->log_file = g_strdup(src->log_file); dest->log_level = src->log_level; +#ifdef ENABLE_THREAD dest->log_lock = src->log_lock; dest->log_lock_attr = src->log_lock_attr; +#endif dest->program_name = src->program_name; dest->enable_syslog = src->enable_syslog; dest->syslog_level = src->syslog_level; @@ -514,6 +516,7 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) dest->enable_pid = src->enable_pid; dest->dump_on_start = src->dump_on_start; +#ifdef LOG_PER_LOGGER_LEVEL if (src->per_logger_level == NULL) { return; @@ -528,6 +531,8 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) dest->per_logger_level->auto_free = 1; } + int i; + for (i = 0; i < src->per_logger_level->count; ++i) { struct log_logger_level *dst_logger = @@ -539,6 +544,7 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) list_add_item(dest->per_logger_level, (tbus) dst_logger); } +#endif } bool_t @@ -591,6 +597,7 @@ internal_log_location_overrides_level(const char *function_name, const char *file_name, enum logLevels *log_level_return) { +#ifdef LOG_PER_LOGGER_LEVEL struct log_logger_level *logger = NULL; int i; @@ -611,6 +618,7 @@ internal_log_location_overrides_level(const char *function_name, return 1; } } +#endif return 0; } @@ -682,11 +690,13 @@ log_config_free(struct log_config *config) { if (config != NULL) { +#ifdef LOG_PER_LOGGER_LEVEL if (config->per_logger_level != NULL) { list_delete(config->per_logger_level); config->per_logger_level = NULL; } +#endif if (0 != config->log_file) { diff --git a/common/log.h b/common/log.h index 0194a530..e77f502c 100644 --- a/common/log.h +++ b/common/log.h @@ -162,6 +162,7 @@ enum logReturns #endif +#ifdef LOG_PER_LOGGER_LEVEL enum log_logger_type { LOG_TYPE_FILE = 0, @@ -174,6 +175,7 @@ struct log_logger_level enum log_logger_type logger_type; char logger_name[LOGGER_NAME_SIZE + 1]; }; +#endif struct log_config { @@ -185,11 +187,15 @@ struct log_config enum logLevels console_level; int enable_syslog; enum logLevels syslog_level; +#ifdef LOG_PER_LOGGER_LEVEL struct list *per_logger_level; +#endif int dump_on_start; int enable_pid; +#ifdef LOG_ENABLE_THREAD pthread_mutex_t log_lock; pthread_mutexattr_t log_lock_attr; +#endif }; /* internal functions, only used in log.c if this ifdef is defined.*/ -- cgit v1.2.3 From 2484928a5a47f5dba5ddd087d893c6c968b35dbb Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 3 Mar 2022 15:37:09 +0000 Subject: Change 3rd parameter of log_start() to flags field --- common/log.c | 147 +++++++++++++++++++++++++++++++++++++++++++------------- common/log.h | 25 ++++++++-- sesman/sesman.c | 5 +- xrdp/xrdp.c | 2 +- 4 files changed, 137 insertions(+), 42 deletions(-) diff --git a/common/log.c b/common/log.c index 8273e25b..c0cc857f 100644 --- a/common/log.c +++ b/common/log.c @@ -224,7 +224,7 @@ internal_log_end(struct log_config *l_cfg) } /** - * Converts a string representing th log level to a value + * Converts a string representing the log level to a value * @param buf * @return */ @@ -492,35 +492,21 @@ internalInitAndAllocStruct(void) return ret; } -void -internal_log_config_copy(struct log_config *dest, const struct log_config *src) -{ - if (src == NULL || dest == NULL) - { - return; - } +/** + * Copies logging levels only from one log_config structure to another + **/ - dest->enable_syslog = src->enable_syslog; - dest->fd = src->fd; - dest->log_file = g_strdup(src->log_file); +static void +internal_log_config_copy_levels(struct log_config *dest, + const struct log_config *src) +{ dest->log_level = src->log_level; -#ifdef ENABLE_THREAD - dest->log_lock = src->log_lock; - dest->log_lock_attr = src->log_lock_attr; -#endif - dest->program_name = src->program_name; dest->enable_syslog = src->enable_syslog; dest->syslog_level = src->syslog_level; dest->enable_console = src->enable_console; dest->console_level = src->console_level; - dest->enable_pid = src->enable_pid; - dest->dump_on_start = src->dump_on_start; #ifdef LOG_PER_LOGGER_LEVEL - if (src->per_logger_level == NULL) - { - return; - } if (dest->per_logger_level == NULL) { dest->per_logger_level = list_create(); @@ -530,6 +516,15 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) } dest->per_logger_level->auto_free = 1; } + else + { + list_clear(dest->per_logger_level); + } + + if (src->per_logger_level == NULL) + { + return; + } int i; @@ -538,15 +533,33 @@ internal_log_config_copy(struct log_config *dest, const struct log_config *src) struct log_logger_level *dst_logger = (struct log_logger_level *)g_malloc(sizeof(struct log_logger_level), 1); - g_memcpy(dst_logger, - (struct log_logger_level *) list_get_item(src->per_logger_level, i), - sizeof(struct log_logger_level)); + *dst_logger = *(struct log_logger_level *) list_get_item(src->per_logger_level, i), - list_add_item(dest->per_logger_level, (tbus) dst_logger); + list_add_item(dest->per_logger_level, (tbus) dst_logger); } #endif } +void +internal_log_config_copy(struct log_config *dest, const struct log_config *src) +{ + if (src != NULL && dest != NULL) + { + dest->fd = src->fd; + g_free(dest->log_file); + dest->log_file = g_strdup(src->log_file); +#ifdef LOG_ENABLE_THREAD + dest->log_lock = src->log_lock; + dest->log_lock_attr = src->log_lock_attr; +#endif + dest->program_name = src->program_name; + dest->enable_pid = src->enable_pid; + dest->dump_on_start = src->dump_on_start; + + internal_log_config_copy_levels(dest, src); + } +} + bool_t internal_log_is_enabled_for_level(const enum logLevels log_level, const bool_t override_destination_level, @@ -710,6 +723,62 @@ log_config_free(struct log_config *config) return LOG_STARTUP_OK; } +/** + * Restarts the logging. + * + * The logging file is never changed, as it is common in xrdp to share a + * log file between parents and children. The end result would be + * confusing for the user. + */ +static enum logReturns +log_restart_from_param(const struct log_config *lc) +{ + enum logReturns rv = LOG_GENERAL_ERROR; + + if (g_staticLogConfig == NULL) + { + log_message(LOG_LEVEL_ALWAYS, "Log not already initialized"); + } + else if (lc == NULL) + { + g_writeln("lc to log_start_from_param is NULL"); + } + else + { + if (g_staticLogConfig->fd >= 0 && + g_strcmp(g_staticLogConfig->log_file, lc->log_file) != 0) + { + log_message(LOG_LEVEL_WARNING, + "Unable to change log file name from %s to %s", + g_staticLogConfig->log_file, + lc->log_file); + } + /* Reconfigure syslog logging, allowing for a program_name change */ + if (g_staticLogConfig->enable_syslog) + { + closelog(); + } + if (lc->enable_syslog) + { + openlog(lc->program_name, LOG_CONS | LOG_PID, LOG_DAEMON); + } + + /* Copy over simple values... */ +#ifdef LOG_ENABLE_THREAD + g_staticLogConfig->log_lock = lc->log_lock; + g_staticLogConfig->log_lock_attr = lc->log_lock_attr; +#endif + g_staticLogConfig->program_name = lc->program_name; + g_staticLogConfig->enable_pid = lc->enable_pid; + g_staticLogConfig->dump_on_start = lc->dump_on_start; + + /* ... and the log levels */ + internal_log_config_copy_levels(g_staticLogConfig, lc); + rv = LOG_STARTUP_OK; + } + return rv; +} + enum logReturns log_start_from_param(const struct log_config *src_log_config) { @@ -758,7 +827,7 @@ log_start_from_param(const struct log_config *src_log_config) */ enum logReturns log_start(const char *iniFile, const char *applicationName, - bool_t dump_on_start) + unsigned int flags) { enum logReturns ret = LOG_GENERAL_ERROR; struct log_config *config; @@ -767,14 +836,24 @@ log_start(const char *iniFile, const char *applicationName, if (config != NULL) { - config->dump_on_start = dump_on_start; - ret = log_start_from_param(config); - log_config_free(config); - - if (ret != LOG_STARTUP_OK) + config->dump_on_start = (flags & LOG_START_DUMP_CONFIG) ? 1 : 0; + if (flags & LOG_START_RESTART) { - g_writeln("Could not start log"); + ret = log_restart_from_param(config); + if (ret != LOG_STARTUP_OK) + { + g_writeln("Could not restart log"); + } + } + else + { + ret = log_start_from_param(config); + if (ret != LOG_STARTUP_OK) + { + g_writeln("Could not start log"); + } } + log_config_free(config); } else { @@ -824,7 +903,7 @@ log_hexdump_with_location(const char *function_name, { char *dump_buffer; enum logReturns rv = LOG_STARTUP_OK; - enum logLevels override_log_level; + enum logLevels override_log_level = LOG_LEVEL_NEVER; bool_t override_destination_level = 0; override_destination_level = internal_log_location_overrides_level( diff --git a/common/log.h b/common/log.h index e77f502c..17615560 100644 --- a/common/log.h +++ b/common/log.h @@ -162,6 +162,22 @@ enum logReturns #endif +/* Flags values for log_start() */ + +/** + * Dump the log config on startup + */ +#define LOG_START_DUMP_CONFIG (1<<0) + +/** + * Restart the logging system. + * + * On a restart, existing files are not closed. This is because the + * files may be shared by sub-processes, and the result will not be what the + * user expects + */ +#define LOG_START_RESTART (1<<1) + #ifdef LOG_PER_LOGGER_LEVEL enum log_logger_type { @@ -179,8 +195,8 @@ struct log_logger_level struct log_config { - const char *program_name; - char *log_file; + const char *program_name; /* Pointer to static storage */ + char *log_file; /* Dynamically allocated */ int fd; enum logLevels log_level; int enable_console; @@ -300,13 +316,12 @@ internal_log_location_overrides_level(const char *function_name, * @param iniFile * @param applicationName the name that is used in the log for the running * application - * @param dump_on_start Whether to dump the config on stdout before - * logging is started + * @param flags Flags to affect the operation of the call * @return LOG_STARTUP_OK on success */ enum logReturns log_start(const char *iniFile, const char *applicationName, - bool_t dump_on_start); + unsigned int flags); /** * An alternative log_start where the caller gives the params directly. diff --git a/sesman/sesman.c b/sesman/sesman.c index e2b057e6..b092d241 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -627,8 +627,9 @@ main(int argc, char **argv) } /* starting logging subsystem */ - log_error = log_start(startup_params.sesman_ini, "xrdp-sesman", - startup_params.dump_config); + log_error = log_start( + startup_params.sesman_ini, "xrdp-sesman", + (startup_params.dump_config) ? LOG_START_DUMP_CONFIG : 0); if (log_error != LOG_STARTUP_OK) { diff --git a/xrdp/xrdp.c b/xrdp/xrdp.c index 22eee5b3..e91672fb 100644 --- a/xrdp/xrdp.c +++ b/xrdp/xrdp.c @@ -538,7 +538,7 @@ main(int argc, char **argv) /* starting logging subsystem */ error = log_start(startup_params.xrdp_ini, "xrdp", - startup_params.dump_config); + (startup_params.dump_config) ? LOG_START_DUMP_CONFIG : 0); if (error != LOG_STARTUP_OK) { -- cgit v1.2.3 From 8bd597a03885ebb23b224995d0c7bc5bf3f0e85c Mon Sep 17 00:00:00 2001 From: matt335672 <30179339+matt335672@users.noreply.github.com> Date: Thu, 3 Mar 2022 15:37:46 +0000 Subject: Fix signal handling in sesman --- sesman/sesman.c | 204 +++++++++++++++++++++++++++++++++++++++---------------- sesman/sesman.h | 20 +++++- sesman/session.c | 13 ++-- sesman/sig.c | 126 ++++++---------------------------- sesman/sig.h | 23 +------ 5 files changed, 195 insertions(+), 191 deletions(-) diff --git a/sesman/sesman.c b/sesman/sesman.c index b092d241..3d15a83b 100644 --- a/sesman/sesman.c +++ b/sesman/sesman.c @@ -52,11 +52,11 @@ struct sesman_startup_params int dump_config; }; -int g_pid; +struct config_sesman *g_cfg; unsigned char g_fixedkey[8] = { 23, 82, 107, 6, 35, 78, 88, 7 }; -struct config_sesman *g_cfg; /* defined in config.h */ - tintptr g_term_event = 0; +tintptr g_sigchld_event = 0; +tintptr g_reload_event = 0; /** * Items stored on the g_con_list @@ -67,8 +67,9 @@ struct sesman_con struct SCP_SESSION *s; }; -static struct trans *g_list_trans = NULL; +static struct trans *g_list_trans; static struct list *g_con_list = NULL; +static int g_pid; /*****************************************************************************/ /** @@ -263,7 +264,7 @@ sesman_close_all(void) struct sesman_con *sc; LOG_DEVEL(LOG_LEVEL_TRACE, "sesman_close_all:"); - trans_delete(g_list_trans); + sesman_delete_listening_transport(); for (index = 0; index < g_con_list->count; index++) { sc = (struct sesman_con *) list_get_item(g_con_list, index); @@ -340,6 +341,86 @@ sesman_listen_conn_in(struct trans *self, struct trans *new_self) return 0; } +/******************************************************************************/ +/** + * Informs the main loop a termination signal has been received + */ +static void +set_term_event(int sig) +{ + /* Don't try to use a wait obj in a child process */ + if (g_getpid() == g_pid) + { + g_set_wait_obj(g_term_event); + } +} + +/******************************************************************************/ +/** + * Informs the main loop a SIGCHLD has been received + */ +static void +set_sigchld_event(int sig) +{ + /* Don't try to use a wait obj in a child process */ + if (g_getpid() == g_pid) + { + g_set_wait_obj(g_sigchld_event); + } +} + +/******************************************************************************/ +/** + * Informs the main loop a SIGHUP has been received + */ +static void +set_reload_event(int sig) +{ + /* Don't try to use a wait obj in a child process */ + if (g_getpid() == g_pid) + { + g_set_wait_obj(g_reload_event); + } +} + +/******************************************************************************/ +void +sesman_delete_listening_transport(void) +{ + trans_delete(g_list_trans); + g_list_trans = NULL; +} + +/******************************************************************************/ +int +sesman_create_listening_transport(const struct config_sesman *cfg) +{ + int rv = 1; + g_list_trans = trans_create(TRANS_MODE_TCP, 8192, 8192); + if (g_list_trans == NULL) + { + LOG(LOG_LEVEL_ERROR, "%s: trans_create failed", __func__); + } + else + { + LOG(LOG_LEVEL_DEBUG, "%s: address %s port %s", + __func__, cfg->listen_address, cfg->listen_port); + rv = trans_listen_address(g_list_trans, cfg->listen_port, + cfg->listen_address); + if (rv != 0) + { + LOG(LOG_LEVEL_ERROR, "%s: trans_listen_address failed", __func__); + sesman_delete_listening_transport(); + } + else + { + g_list_trans->trans_conn_in = sesman_listen_conn_in; + } + } + + return rv; +} + /******************************************************************************/ /** * @@ -352,7 +433,6 @@ sesman_main_loop(void) int error; int robjs_count; int wobjs_count; - int cont; int timeout; int index; intptr_t robjs[32]; @@ -365,33 +445,22 @@ sesman_main_loop(void) LOG(LOG_LEVEL_ERROR, "sesman_main_loop: list_create failed"); return 1; } - g_list_trans = trans_create(TRANS_MODE_TCP, 8192, 8192); - if (g_list_trans == NULL) + if (sesman_create_listening_transport(g_cfg) != 0) { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: trans_create failed"); + LOG(LOG_LEVEL_ERROR, + "sesman_main_loop: sesman_create_listening_transport failed"); list_delete(g_con_list); return 1; } - LOG(LOG_LEVEL_DEBUG, "sesman_main_loop: address %s port %s", - g_cfg->listen_address, g_cfg->listen_port); - error = trans_listen_address(g_list_trans, g_cfg->listen_port, - g_cfg->listen_address); - if (error != 0) - { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: trans_listen_address " - "failed"); - trans_delete(g_list_trans); - list_delete(g_con_list); - return 1; - } - g_list_trans->trans_conn_in = sesman_listen_conn_in; - cont = 1; - while (cont) + error = 0; + while (!error) { timeout = -1; robjs_count = 0; robjs[robjs_count++] = g_term_event; + robjs[robjs_count++] = g_sigchld_event; + robjs[robjs_count++] = g_reload_event; wobjs_count = 0; for (index = 0; index < g_con_list->count; index++) { @@ -413,34 +482,53 @@ sesman_main_loop(void) { break; } - error = trans_get_wait_objs_rw(g_list_trans, robjs, &robjs_count, - wobjs, &wobjs_count, &timeout); - if (error != 0) + if (g_list_trans != NULL) { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_get_wait_objs_rw failed"); - break; + /* g_list_trans might be NULL on a reconfigure if sesman + * is unable to listen again */ + error = trans_get_wait_objs_rw(g_list_trans, robjs, &robjs_count, + wobjs, &wobjs_count, &timeout); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "trans_get_wait_objs_rw failed"); + break; + } } - error = g_obj_wait(robjs, robjs_count, wobjs, wobjs_count, timeout); - if (error != 0) + if (g_obj_wait(robjs, robjs_count, wobjs, wobjs_count, timeout) != 0) { - /* error, should not get here */ + /* should not get here */ + LOG(LOG_LEVEL_WARNING, "sesman_main_loop: " + "Unexpected error from g_obj_wait()"); g_sleep(100); } if (g_is_wait_obj_set(g_term_event)) /* term */ { + LOG(LOG_LEVEL_INFO, "sesman_main_loop: " + "sesman asked to terminate"); break; } + if (g_is_wait_obj_set(g_sigchld_event)) /* A child has exited */ + { + g_reset_wait_obj(g_sigchld_event); + sig_sesman_session_end(); + } + + if (g_is_wait_obj_set(g_reload_event)) /* We're asked to reload */ + { + g_reset_wait_obj(g_reload_event); + sig_sesman_reload_cfg(); + } + for (index = 0; index < g_con_list->count; index++) { scon = (struct sesman_con *)list_get_item(g_con_list, index); if (scon != NULL) { - error = trans_check_wait_objs(scon->t); - if (error != 0) + if (trans_check_wait_objs(scon->t) != 0) { LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " "trans_check_wait_objs failed, removing trans"); @@ -451,12 +539,16 @@ sesman_main_loop(void) } } } - error = trans_check_wait_objs(g_list_trans); - if (error != 0) + + if (g_list_trans != NULL) { - LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " - "trans_check_wait_objs failed"); - break; + error = trans_check_wait_objs(g_list_trans); + if (error != 0) + { + LOG(LOG_LEVEL_ERROR, "sesman_main_loop: " + "trans_check_wait_objs failed"); + break; + } } } for (index = 0; index < g_con_list->count; index++) @@ -465,7 +557,7 @@ sesman_main_loop(void) delete_connection(scon); } list_delete(g_con_list); - trans_delete(g_list_trans); + sesman_delete_listening_transport(); return 0; } @@ -710,20 +802,17 @@ main(int argc, char **argv) /* signal handling */ g_pid = g_getpid(); - /* old style signal handling is now managed synchronously by a - * separate thread. uncomment this block if you need old style - * signal handling and comment out thread_sighandler_start() - * going back to old style for the time being - * problem with the sigaddset functions in sig.c - jts */ -#if 1 - g_signal_hang_up(sig_sesman_reload_cfg); /* SIGHUP */ - g_signal_user_interrupt(sig_sesman_shutdown); /* SIGINT */ - g_signal_terminate(sig_sesman_shutdown); /* SIGTERM */ - g_signal_child_stop(sig_sesman_session_end); /* SIGCHLD */ -#endif -#if 0 - thread_sighandler_start(); -#endif + g_snprintf(text, 255, "xrdp_sesman_%8.8x_main_term", g_pid); + g_term_event = g_create_wait_obj(text); + g_snprintf(text, 255, "xrdp_sesman_%8.8x_sigchld", g_pid); + g_sigchld_event = g_create_wait_obj(text); + g_snprintf(text, 255, "xrdp_sesman_%8.8x_reload", g_pid); + g_reload_event = g_create_wait_obj(text); + + g_signal_hang_up(set_reload_event); /* SIGHUP */ + g_signal_user_interrupt(set_term_event); /* SIGINT */ + g_signal_terminate(set_term_event); /* SIGTERM */ + g_signal_child_stop(set_sigchld_event); /* SIGCHLD */ if (daemon) { @@ -765,9 +854,6 @@ main(int argc, char **argv) g_chmod_hex("/tmp/.X11-unix", 0x1777); } - g_snprintf(text, 255, "xrdp_sesman_%8.8x_main_term", g_pid); - g_term_event = g_create_wait_obj(text); - error = sesman_main_loop(); /* clean up PID file on exit */ @@ -776,6 +862,8 @@ main(int argc, char **argv) g_file_delete(pid_file); } + g_delete_wait_obj(g_reload_event); + g_delete_wait_obj(g_sigchld_event); g_delete_wait_obj(g_term_event); if (!daemon) diff --git a/sesman/sesman.h b/sesman/sesman.h index 0c0a4c6d..e7e7fe63 100644 --- a/sesman/sesman.h +++ b/sesman/sesman.h @@ -45,8 +45,8 @@ extern struct config_sesman *g_cfg; extern unsigned char g_fixedkey[8]; extern tintptr g_term_event; -extern int g_pid; - +extern tintptr g_sigchld_event; +extern tintptr g_reload_event; /* * Close all file descriptors used by sesman. @@ -60,4 +60,20 @@ extern int g_pid; int sesman_close_all(void); +/* + * Remove the listening transport + * + * Needed if reloading the config and the listener has changed + */ +void +sesman_delete_listening_transport(void); + +/* + * Create the listening socket transport + * + * @return 0 for success + */ +int +sesman_create_listening_transport(const struct config_sesman *cfg); + #endif diff --git a/sesman/session.c b/sesman/session.c index 10b8a16d..8be3855a 100644 --- a/sesman/session.c +++ b/sesman/session.c @@ -47,10 +47,8 @@ #define PR_SET_NO_NEW_PRIVS 38 #endif -struct session_chain *g_sessions; -int g_session_count; - -extern tbus g_term_event; /* in sesman.c */ +static struct session_chain *g_sessions; +static int g_session_count; /** * Creates a string consisting of all parameters that is hosted in the param list @@ -522,8 +520,13 @@ session_start_fork(tbus data, tui8 type, struct SCP_SESSION *s) "Failed to clone the session data - out of memory"); g_exit(1); } - auth_start_session(data, display); + + /* Wait objects created in a parent are not valid in a child */ + g_delete_wait_obj(g_reload_event); + g_delete_wait_obj(g_sigchld_event); g_delete_wait_obj(g_term_event); + + auth_start_session(data, display); sesman_close_all(); g_sprintf(geometry, "%dx%d", s->width, s->height); g_sprintf(depth, "%d", s->bpp); diff --git a/sesman/sig.c b/sesman/sig.c index 0c4c4483..14d4c820 100644 --- a/sesman/sig.c +++ b/sesman/sig.c @@ -28,48 +28,17 @@ #include #endif -#include - +#include "string_calls.h" #include "sesman.h" /******************************************************************************/ void -sig_sesman_shutdown(int sig) -{ - char pid_file[256]; - - LOG(LOG_LEVEL_INFO, "shutting down sesman %d", 1); - - if (g_getpid() != g_pid) - { - LOG_DEVEL(LOG_LEVEL_DEBUG, "g_getpid() [%d] differs from g_pid [%d]", (g_getpid()), g_pid); - return; - } - - LOG_DEVEL(LOG_LEVEL_DEBUG, " - getting signal %d pid %d", sig, g_getpid()); - - g_set_wait_obj(g_term_event); - - session_sigkill_all(); - - g_snprintf(pid_file, 255, "%s/xrdp-sesman.pid", XRDP_PID_PATH); - g_file_delete(pid_file); -} - -/******************************************************************************/ -void -sig_sesman_reload_cfg(int sig) +sig_sesman_reload_cfg(void) { int error; struct config_sesman *cfg; - LOG(LOG_LEVEL_WARNING, "receiving SIGHUP %d", 1); - - if (g_getpid() != g_pid) - { - LOG_DEVEL(LOG_LEVEL_DEBUG, "g_getpid() [%d] differs from g_pid [%d]", g_getpid(), g_pid); - return; - } + LOG(LOG_LEVEL_INFO, "receiving SIGHUP"); if ((cfg = config_read(g_cfg->sesman_ini)) == NULL) { @@ -77,8 +46,18 @@ sig_sesman_reload_cfg(int sig) return; } - /* stop logging subsystem */ - log_end(); + /* Deal with significant config changes */ + if (g_strcmp(g_cfg->listen_address, cfg->listen_address) != 0 || + g_strcmp(g_cfg->listen_port, cfg->listen_port) != 0) + { + LOG(LOG_LEVEL_INFO, "sesman listen address changed to %s:%s", + cfg->listen_address, cfg->listen_port); + + /* We have to delete the old port before listening to the new one + * in case they overlap in scope */ + sesman_delete_listening_transport(); + sesman_create_listening_transport(cfg); + } /* free old config data */ config_free(g_cfg); @@ -86,8 +65,8 @@ sig_sesman_reload_cfg(int sig) /* replace old config with newly read one */ g_cfg = cfg; - /* start again logging subsystem */ - error = log_start(g_cfg->sesman_ini, "xrdp-sesman", 0); + /* Restart logging subsystem */ + error = log_start(g_cfg->sesman_ini, "xrdp-sesman", LOG_START_RESTART); if (error != LOG_STARTUP_OK) { @@ -109,84 +88,21 @@ sig_sesman_reload_cfg(int sig) /******************************************************************************/ void -sig_sesman_session_end(int sig) +sig_sesman_session_end(void) { int pid; - if (g_getpid() != g_pid) - { - return; - } - + LOG(LOG_LEVEL_DEBUG, "receiving SIGCHLD"); do { pid = g_waitchild(); if (pid > 0) { + LOG(LOG_LEVEL_INFO, "Process %d has exited", pid); + session_kill(pid); } } while (pid > 0); } - -/******************************************************************************/ -void * -sig_handler_thread(void *arg) -{ - int recv_signal; - sigset_t sigmask; - sigset_t oldmask; - sigset_t waitmask; - - /* mask signals to be able to wait for them... */ - sigfillset(&sigmask); - /* it is a good idea not to block SIGILL SIGSEGV */ - /* SIGFPE -- see sigaction(2) NOTES */ - pthread_sigmask(SIG_BLOCK, &sigmask, &oldmask); - - /* building the signal wait mask... */ - sigemptyset(&waitmask); - sigaddset(&waitmask, SIGHUP); - sigaddset(&waitmask, SIGCHLD); - sigaddset(&waitmask, SIGTERM); - sigaddset(&waitmask, SIGINT); - - // sigaddset(&waitmask, SIGFPE); - // sigaddset(&waitmask, SIGILL); - // sigaddset(&waitmask, SIGSEGV); - - do - { - LOG_DEVEL(LOG_LEVEL_DEBUG, "calling sigwait()"); - sigwait(&waitmask, &recv_signal); - - switch (recv_signal) - { - case SIGHUP: - //reload cfg - //we must stop & restart logging, or copy logging cfg!!!! - LOG_DEVEL(LOG_LEVEL_DEBUG, "sesman received SIGHUP"); - //return 0; - break; - case SIGCHLD: - /* a session died */ - LOG_DEVEL(LOG_LEVEL_DEBUG, "sesman received SIGCHLD"); - sig_sesman_session_end(SIGCHLD); - break; - case SIGINT: - /* we die */ - LOG_DEVEL(LOG_LEVEL_DEBUG, "sesman received SIGINT"); - sig_sesman_shutdown(recv_signal); - break; - case SIGTERM: - /* we die */ - LOG_DEVEL(LOG_LEVEL_DEBUG, "sesman received SIGTERM"); - sig_sesman_shutdown(recv_signal); - break; - } - } - while (1); - - return 0; -} diff --git a/sesman/sig.h b/sesman/sig.h index f7582686..447dde63 100644 --- a/sesman/sig.h +++ b/sesman/sig.h @@ -27,39 +27,20 @@ #ifndef SIG_H #define SIG_H -/** - * - * @brief Shutdown signal code - * @param sig The received signal - * - */ -void -sig_sesman_shutdown(int sig); - /** * * @brief SIGHUP handling code - * @param sig The received signal * */ void -sig_sesman_reload_cfg(int sig); +sig_sesman_reload_cfg(void); /** * * @brief SIGCHLD handling code - * @param sig The received signal * */ void -sig_sesman_session_end(int sig); - -/** - * - * @brief signal handling thread - * - */ -void * -sig_handler_thread(void *arg); +sig_sesman_session_end(void); #endif -- cgit v1.2.3 From 0ad7bac6935110c505c4e80e390c49c5b6f32140 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 7 Feb 2022 15:53:08 +0900 Subject: Update NEWS for v0.9.18.1 --- NEWS.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/NEWS.md b/NEWS.md index d0e60959..77d7b21b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,17 @@ +# Release notes for xrdp v0.9.18.1 (2022/02/08) + +This is a security fix release that includes fixes for the following privilege escalation vulnerability. + +* [CVE-2022-23613: Privilege escalation on xrdp-sesman](https://www.cve.org/CVERecord?id=CVE-2022-23613) + +Users who uses xrdp v0.9.17 or v0.9.18 are recommended to update to this version. + +## Special thanks + +Thanks to [Gilad Kleinman](https://github.com/giladkl) reporting the vulnerability and reviewing fix. + +----------------------- + # Release notes for xrdp v0.9.18 (2022/01/10) ## General announcements -- cgit v1.2.3 From 68abf67a059c26dd4fd7e419ca39e72d987dadf7 Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 7 Mar 2022 15:45:46 +0900 Subject: Update NEWS for v0.9.19 --- NEWS.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/NEWS.md b/NEWS.md index 77d7b21b..cc483986 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,40 @@ +# Release notes for xrdp v0.9.19 (2022/03/17) + +## General announcements +* Running xrdp and xrdp-sesman on separate hosts is still supported by this release, but is now deprecated. This is not secure. A future release will replace the TCP socket used between these processes with a Unix Domain Socket, and then cross-host running will not be possible. + +## New features +* Both inbound and outbound clipboards can now be restricted for text, files or images [Sponsored by @CyberTrust @clear-code and @kenhys] (#2087) + +## Bug fixes +* [CVE-2022-23613](https://www.cve.org/CVERecord?id=CVE-2022-23613): Privilege escalation on xrdp-sesman (This fix is also in the out-of-band v0.9.18.1 release) +* The versions of imlib2 used on RHEL 7 and 8 are now detected correctly (#2118) +* Some situations where zombie processes could exist have been resolved (#2146, #2151, #2168) +* Some null-pointer exceptions which can happen in the logging module have been addressed (#2149) +* Some minor logging errors have been corrected (#2152) +* The signal handling in sesman has been reworked to prevent race conditions when a child exits. This has also made it possible to reliably reload the sesman configuration with SIGHUP (#1729, #2168) + +## Internal changes +* Versions 0.13 and later of checklib can undefine the pre-processor symbol `HAVE_STDINT_H`. The xrdp tests now build successfully against these versions (#2124) +* OpenSSL packaging changes (#2130):- + - The OpenSSL 3 EVP interface is now fully supported + - When building against OpenSSL 3, an internal implementation of the RC4 cipher is used instead of the implementation from the OpenSSL legacy provider + - The wrapping of the OpenSSL library has been improved which should make it simpler to provide an alternative cryptographic provider in the future, if required + - The logging of TLS/non-TLS security negotiation has been improved +* cppcheck version used for CI bumped to 2.7 (#2140) +* The `s_check()` macro which is easily mis-used has been removed (#2144) +* Status values for the DRDYNVC channel are now available in `libxrdp/xrdp_channel.h` + +## Changes for packagers or developers +* On OpenSSL 3 systems, there is now no need to build with the `-Wno-error=deprecated-declarations` flag + +## Known issues + +* On-the-fly resolution change requires the Microsoft Store version of Remote Desktop client but sometimes crashes on connect (#1869) +* xrdp's login dialog is not relocated at the center of the new resolution after on-the-fly resolution change happens (#1867) + +----------------------- + # Release notes for xrdp v0.9.18.1 (2022/02/08) This is a security fix release that includes fixes for the following privilege escalation vulnerability. -- cgit v1.2.3 From 3fa6e9852be1f633f0d6bd30ebadf4bffe9a73bd Mon Sep 17 00:00:00 2001 From: Koichiro IWAO Date: Mon, 7 Mar 2022 15:46:17 +0900 Subject: Bump version to v0.9.19 --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index a7a10bf6..015f81a8 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.18], [xrdp-devel@googlegroups.com]) +AC_INIT([xrdp], [0.9.19], [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]) -- cgit v1.2.3