File libssh-fix-ipv6-hostname-regression.patch of Package libssh
Ported the following with a few extra test functions
From 66ac6343b246458a6645ae32f75556a1407031ec Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 22 Dec 2023 10:32:40 +0100
Subject: [PATCH 1/2] Fix regression in IPv6 addresses in hostname parsing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/config_parser.h | 11 ++++++++---
src/config.c | 4 ++--
src/config_parser.c | 19 ++++++++++++++-----
src/options.c | 10 ++--------
4 files changed, 26 insertions(+), 18 deletions(-)
Index: libssh-0.9.8/include/libssh/config_parser.h
===================================================================
--- libssh-0.9.8.orig/include/libssh/config_parser.h
+++ libssh-0.9.8/include/libssh/config_parser.h
@@ -26,6 +26,8 @@
#ifndef CONFIG_PARSER_H_
#define CONFIG_PARSER_H_
+#include <stdbool.h>
+
char *ssh_config_get_cmd(char **str);
char *ssh_config_get_token(char **str);
@@ -45,13 +47,16 @@ int ssh_config_get_yesno(char **str, int
* be stored or NULL if we do not care about the result.
* @param[out] port Pointer to the location, where the new port will
* be stored or NULL if we do not care about the result.
+ * @param[in] ignore_port Set to true if the we should not attempt to parse
+ * port number.
*
* @returns SSH_OK if the provided string is in format of SSH URI,
* SSH_ERROR on failure
*/
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port);
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port);
#endif /* LIBSSH_CONFIG_H_ */
Index: libssh-0.9.8/src/config.c
===================================================================
--- libssh-0.9.8.orig/src/config.c
+++ libssh-0.9.8/src/config.c
@@ -324,7 +324,7 @@ ssh_config_parse_proxy_jump(ssh_session
}
if (parse_entry) {
/* We actually care only about the first item */
- rv = ssh_config_parse_uri(cp, &username, &hostname, &port);
+ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
/* The rest of the list needs to be passed on */
if (endp != NULL) {
next = strdup(endp + 1);
@@ -335,7 +335,7 @@ ssh_config_parse_proxy_jump(ssh_session
}
} else {
/* The rest is just sanity-checked to avoid failures later */
- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL);
+ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
}
if (rv != SSH_OK) {
goto out;
Index: libssh-0.9.8/src/config_parser.c
===================================================================
--- libssh-0.9.8.orig/src/config_parser.c
+++ libssh-0.9.8/src/config_parser.c
@@ -133,10 +133,14 @@ int ssh_config_get_yesno(char **str, int
return notfound;
}
+/* Parse the URI extracting parts such as a username, hostname and port.
+ * If the port is NULL, do not expect port present and be more lax for example
+ * with matching IPv6 address which have the same separators as host:port */
int ssh_config_parse_uri(const char *tok,
- char **username,
- char **hostname,
- char **port)
+ char **username,
+ char **hostname,
+ char **port,
+ bool ignore_port)
{
char *endp = NULL;
long port_n;
@@ -182,12 +186,17 @@ int ssh_config_parse_uri(const char *tok
if (endp == NULL) {
goto error;
}
- } else {
- /* Hostnames or aliases expand to the last colon or to the end */
+ } else if (!ignore_port) {
+ /* Hostnames or aliases expand to the last colon (if port is requested)
+ * or to the end */
endp = strrchr(tok, ':');
if (endp == NULL) {
endp = strchr(tok, '\0');
}
+ } else {
+ /* If no port is requested, expand to the end of line
+ * (to accommodate the IPv6 addresses) */
+ endp = strchr(tok, '\0');
}
if (tok == endp) {
/* Zero-length hostnames are not valid */
Index: libssh-0.9.8/src/options.c
===================================================================
--- libssh-0.9.8.orig/src/options.c
+++ libssh-0.9.8/src/options.c
@@ -491,17 +491,11 @@ int ssh_options_set(ssh_session session,
ssh_set_error_invalid(session);
return -1;
} else {
- char *username = NULL, *hostname = NULL, *port = NULL;
- rc = ssh_config_parse_uri(value, &username, &hostname, &port);
+ char *username = NULL, *hostname = NULL;
+ rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
if (rc != SSH_OK) {
return -1;
}
- if (port != NULL) {
- SAFE_FREE(username);
- SAFE_FREE(hostname);
- SAFE_FREE(port);
- return -1;
- }
if (username != NULL) {
SAFE_FREE(session->opts.username);
session->opts.username = username;
Index: libssh-0.9.8/tests/unittests/torture_config.c
===================================================================
--- libssh-0.9.8.orig/tests/unittests/torture_config.c
+++ libssh-0.9.8/tests/unittests/torture_config.c
@@ -7,6 +7,7 @@
#include "libssh/session.h"
#include "libssh/config_parser.h"
#include "match.c"
+#include <errno.h>
extern LIBSSH_THREAD int ssh_log_level;
@@ -199,7 +200,7 @@ static int setup_config_files(void **sta
return 0;
}
-static int teardown(void **state)
+static int teardown_config_files(void **state)
{
unlink(LIBSSH_TESTCONFIG1);
unlink(LIBSSH_TESTCONFIG2);
@@ -220,6 +221,32 @@ static int teardown(void **state)
return 0;
}
+static int setup(void **state)
+{
+ ssh_session session = NULL;
+ char *wd = NULL;
+ int verbosity;
+
+ session = ssh_new();
+
+ verbosity = torture_libssh_verbosity();
+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
+ wd = torture_get_current_working_dir();
+ ssh_options_set(session, SSH_OPTIONS_SSH_DIR, wd);
+ free(wd);
+
+ *state = session;
+
+ return 0;
+}
+
+static int teardown(void **state)
+{
+ ssh_free(*state);
+
+ return 0;
+}
+
/**
* @brief tests ssh_config_parse_file with Include directives
*/
@@ -997,6 +1024,52 @@ static void torture_config_match_pattern
}
+static void torture_config_parse_uri(void **state)
+{
+ char *username = NULL;
+ char *hostname = NULL;
+ char *port = NULL;
+ int rc;
+
+ (void)state; /* unused */
+
+ rc = ssh_config_parse_uri("localhost", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "localhost");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_null(port);
+
+ rc = ssh_config_parse_uri("1.2.3.4:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1.2.3.4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ rc = ssh_config_parse_uri("[1:2:3::4]:2222", &username, &hostname, &port, false);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+ assert_string_equal(port, "2222");
+ SAFE_FREE(port);
+
+ /* do not want port */
+ rc = ssh_config_parse_uri("1:2:3::4", &username, &hostname, NULL, true);
+ assert_return_code(rc, errno);
+ assert_null(username);
+ assert_string_equal(hostname, "1:2:3::4");
+ SAFE_FREE(hostname);
+}
int torture_run_tests(void) {
int rc;
@@ -1012,12 +1085,14 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_config_rekey),
cmocka_unit_test(torture_config_pubkeyacceptedkeytypes),
cmocka_unit_test(torture_config_match_pattern),
+ cmocka_unit_test_setup_teardown(torture_config_parse_uri,
+ setup, teardown),
};
ssh_init();
torture_filter_tests(tests);
- rc = cmocka_run_group_tests(tests, setup_config_files, teardown);
+ rc = cmocka_run_group_tests(tests, setup_config_files, teardown_config_files);
ssh_finalize();
return rc;
}
Index: libssh-0.9.8/tests/unittests/torture_options.c
===================================================================
--- libssh-0.9.8.orig/tests/unittests/torture_options.c
+++ libssh-0.9.8/tests/unittests/torture_options.c
@@ -59,6 +59,20 @@ static void torture_options_set_host(voi
assert_non_null(session->opts.host);
assert_string_equal(session->opts.host, "localhost");
+ /* IPv4 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.1.1.1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "127.1.1.1");
+ assert_null(session->opts.username);
+
+ /* IPv6 address */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "::1");
+ assert_true(rc == 0);
+ assert_non_null(session->opts.host);
+ assert_string_equal(session->opts.host, "::1");
+ assert_null(session->opts.username);
+
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation");
assert_true(rc == 0);
assert_non_null(session->opts.host);