libssh/CVE-2026-59846.patch
Pavol Žáčik 9e3f4b36c8
Backport CVE patches from 0.11.5
Resolves: RHEL-213033
Resolves: RHEL-213041
Resolves: RHEL-213047
Resolves: RHEL-213081
Resolves: RHEL-213114
Resolves: RHEL-213119
Resolves: RHEL-213151
2026-07-30 10:04:06 +02:00

318 lines
10 KiB
Diff

From d18c05327e8a6e9e8f7ddb26021a4fb7860e4afd Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Wed, 27 Dec 2023 20:32:18 +0100
Subject: [PATCH 04/12] misc: Add function to check username syntax
Malicious code can be injected using the username with metacharacters,
therefore the username must be validated before using it with any %u.
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/misc.h | 1 +
src/misc.c | 32 ++++++++++++++++++++++++++++++++
tests/unittests/torture_misc.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index 3452251d..06c62678 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -103,6 +103,7 @@ int ssh_tmpname(char *template);
char *ssh_strreplace(const char *src, const char *pattern, const char *repl);
int ssh_check_hostname_syntax(const char *hostname);
+int ssh_check_username_syntax(const char *username);
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
diff --git a/src/misc.c b/src/misc.c
index 0cca373a..0389ee70 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -2148,4 +2148,36 @@ FILE *ssh_strict_fopen(const char *filename, size_t max_file_size)
return f;
}
+/**
+ * @brief Checks syntax of a username
+ *
+ * This check disallows metacharacters in the username
+ *
+ * @param username The username to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the username passes syntax check
+ * SSH_ERROR otherwise or if username is NULL or empty string
+ */
+int ssh_check_username_syntax(const char *username)
+{
+ size_t username_len;
+
+ if (username == NULL || *username == '-') {
+ return SSH_ERROR;
+ }
+
+ username_len = strlen(username);
+ if (username_len == 0 || username[username_len - 1] == '\\' ||
+ strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ return SSH_ERROR;
+ }
+ for (size_t i = 0; i < username_len; i++) {
+ if (isspace(username[i]) != 0 && username[i + 1] == '-') {
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
/** @} */
diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c
index 4470c358..7c134304 100644
--- a/tests/unittests/torture_misc.c
+++ b/tests/unittests/torture_misc.c
@@ -839,6 +839,39 @@ static void torture_ssh_check_hostname_syntax(void **state)
assert_int_equal(rc, SSH_ERROR);
}
+static void torture_ssh_check_username_syntax(void **state) {
+ int rc;
+ (void)state;
+
+ rc = ssh_check_username_syntax("username");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice and Bob");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("n4me?");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_check_username_syntax("alice&bob");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("backslash\\");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("&var|()us\"<ha`r{}'");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(" -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("me and -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("los -santos");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("- who?");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(NULL);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
static void torture_ssh_is_ipaddr(void **state) {
int rc;
char *interf = malloc(64);
@@ -932,6 +965,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_strreplace),
cmocka_unit_test(torture_ssh_strerror),
cmocka_unit_test(torture_ssh_check_hostname_syntax),
+ cmocka_unit_test(torture_ssh_check_username_syntax),
cmocka_unit_test(torture_ssh_is_ipaddr),
cmocka_unit_test(torture_ssh_get_hexa),
};
--
2.54.0
From b32217c6ed3a5a47e42f89fb68fe7ffec2c8f4bb Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Thu, 28 Dec 2023 12:16:29 +0100
Subject: [PATCH 05/12] Check any input username for validity
Check possible inputs of username for malicious code.
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
src/config_parser.c | 4 ++++
src/misc.c | 10 ++++++++--
src/options.c | 5 +++++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/config_parser.c b/src/config_parser.c
index 5555f6fd..9163d374 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -203,6 +203,10 @@ int ssh_config_parse_uri(const char *tok,
if (*username == NULL) {
goto error;
}
+ rc = ssh_check_username_syntax(*username);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
tok = endp + 1;
/* If there is second @ character, this does not look like our URI */
diff --git a/src/misc.c b/src/misc.c
index 0389ee70..b63b5521 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -183,6 +183,7 @@ char *ssh_get_local_username(void)
{
DWORD size = 0;
char *user;
+ int rc;
/* get the size */
GetUserName(NULL, &size);
@@ -193,7 +194,10 @@ char *ssh_get_local_username(void)
}
if (GetUserName(user, &size)) {
- return user;
+ rc = ssh_check_username_syntax(user);
+ if (rc == SSH_OK) {
+ return user;
+ }
}
return NULL;
@@ -337,8 +341,10 @@ char *ssh_get_local_username(void)
}
name = strdup(pwd.pw_name);
+ rc = ssh_check_username_syntax(name);
- if (name == NULL) {
+ if (rc != SSH_OK) {
+ free(name);
return NULL;
}
diff --git a/src/options.c b/src/options.c
index a8023fbf..a40459ff 100644
--- a/src/options.c
+++ b/src/options.c
@@ -662,6 +662,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ rc = ssh_check_username_syntax(session->opts.username);
+ if (rc != SSH_OK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
}
break;
case SSH_OPTIONS_SSH_DIR:
--
2.54.0
From 26ae62df976dfd381b72f201ed4281530cd63d86 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Thu, 28 Dec 2023 12:27:31 +0100
Subject: [PATCH 06/12] torture: Add cases for username checks
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_config.c | 3 +++
tests/unittests/torture_options.c | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 6dc59e48..09c769b9 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -2293,6 +2293,9 @@ static void torture_config_parse_uri(void **state)
assert_null(username);
assert_string_equal(hostname, "1:2:3::4");
SAFE_FREE(hostname);
+
+ rc = ssh_config_parse_uri("user -name@", &username, NULL, NULL, true);
+ assert_int_equal(rc, SSH_ERROR);
}
/* Invalid configuration files
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index 2a7a04de..5c3d3f2c 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -89,6 +89,9 @@ static void torture_options_set_host(void **state) {
assert_non_null(session->opts.username);
assert_string_equal(session->opts.username, "at@login");
+ /* disallow metacharacters in the username */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "shallN()tP4ss -@hostname");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
}
static void torture_options_set_ciphers(void **state) {
@@ -396,6 +399,9 @@ static void torture_options_set_user(void **state) {
assert_true(rc == 0);
#endif /* _WIN32 */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, "&shallN()tP4ss");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
rc = ssh_options_set(session, SSH_OPTIONS_USER, "guru");
assert_true(rc == 0);
assert_string_equal(session->opts.username, "guru");
--
2.54.0
From 08a4f4c5151e75eda6d2105db493aa8c8df0dee7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 2 Apr 2026 15:39:25 +0200
Subject: [PATCH 07/12] CVE-2026-59846 Block shell metacharacters from
usernames
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When an attacker could sneak the dollar sign or backslash into the username
expanded for example in proxy command, it can result in printing environment
variables that might contain secrets.
This is a fixup of CVE-2023-6004 which fixed this for hostnames, but these
two metacharacters were left out from the username filter.
This keeps the list in one place to simplify maintenance.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 6309df220e3431deb41946f892f4bb5af8b59dba)
---
include/libssh/priv.h | 2 ++
src/misc.c | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index 876ea4ed..22f1e6da 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -223,6 +223,8 @@ int ssh_gettimeofday(struct timeval *__p, void *__t);
# define LIBSSH_MEM_PROTECTION
#endif
+#define SSH_DANGEROUS_SHELL_CHARS "'`\";&<>|(){}$\\,"
+
/* forward declarations */
struct ssh_common_struct;
struct ssh_kex_struct;
diff --git a/src/misc.c b/src/misc.c
index b63b5521..f4f48a37 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -2174,7 +2174,7 @@ int ssh_check_username_syntax(const char *username)
username_len = strlen(username);
if (username_len == 0 || username[username_len - 1] == '\\' ||
- strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ strpbrk(username, SSH_DANGEROUS_SHELL_CHARS) != NULL) {
return SSH_ERROR;
}
for (size_t i = 0; i < username_len; i++) {
--
2.54.0