libssh/CVE-2026-59846.patch
Pavol Žáčik a97adc51f9
Backport CVE patches from 0.11.5
Resolves: RHEL-215674
Resolves: RHEL-215675
Resolves: RHEL-215676
Resolves: RHEL-215677
Resolves: RHEL-215678
Resolves: RHEL-215679
Resolves: RHEL-215680

Assisted-by: Ymir
2026-07-30 12:22:19 +02:00

256 lines
8.3 KiB
Diff

From 18ca8c90663b800a21bdce39dab4e402b8ad4bc0 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Wed, 27 Dec 2023 20:32:18 +0100
Subject: [PATCH 1/3] 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 a5bee930..c8328722 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -98,5 +98,6 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len);
int ssh_newline_vis(const char *string, char *buf, size_t buf_len);
int ssh_check_hostname_syntax(const char *hostname);
+int ssh_check_username_syntax(const char *username);
#endif /* MISC_H_ */
diff --git a/src/misc.c b/src/misc.c
index 8bbb2587..23fad83d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1835,4 +1835,36 @@ int ssh_check_hostname_syntax(const char *hostname)
return SSH_OK;
}
+/**
+ * @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 f16b766e..3737ed27 100644
--- a/tests/unittests/torture_misc.c
+++ b/tests/unittests/torture_misc.c
@@ -735,6 +735,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);
@@ -796,6 +829,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_mkdirs),
cmocka_unit_test(torture_ssh_quote_file_name),
cmocka_unit_test(torture_ssh_check_hostname_syntax),
+ cmocka_unit_test(torture_ssh_check_username_syntax),
cmocka_unit_test(torture_ssh_is_ipaddr),
};
From e02b26abd4b34aac065beb1860c54782055334ac Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Thu, 28 Dec 2023 12:16:29 +0100
Subject: [PATCH 2/3] 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 9e07a0b7..3c43b806 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -166,6 +166,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 23fad83d..c1c4dcfa 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -178,6 +178,7 @@ int gettimeofday(struct timeval *__p, void *__t) {
char *ssh_get_local_username(void) {
DWORD size = 0;
char *user;
+ int rc;
/* get the size */
GetUserName(NULL, &size);
@@ -188,7 +189,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;
@@ -330,8 +334,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 082c6cf4..f7eaab42 100644
--- a/src/options.c
+++ b/src/options.c
@@ -595,6 +595,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:
From bf16e4df1809e1fa12aaff2fab6c43b31a108011 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 2 Apr 2026 15:39:25 +0200
Subject: [PATCH 3/3] 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 3e910860..dee941c4 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -218,6 +218,8 @@ int 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 c1c4dcfa..11f799e1 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -1861,7 +1861,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++) {