Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package libssh for openSUSE:Factory checked 
in at 2026-09-23 14:34:07
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/libssh (Old)
 and      /work/SRC/openSUSE:Factory/.libssh.new.383539 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "libssh"

Wed Sep 23 14:34:07 2026 rev:85 rq:1379402 version:0.11.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/libssh/libssh.changes    2026-08-15 
22:40:50.105027672 +0200
+++ /work/SRC/openSUSE:Factory/.libssh.new.383539/libssh.changes        
2026-09-23 14:35:23.021420715 +0200
@@ -1,0 +2,11 @@
+Mon Sep 14 11:06:12 UTC 2026 - Pedro Monreal <[email protected]>
+
+- Fix: libssh ignores explicit username in URL if User specified
+  in SSH config (bsc#1279934)
+  * options: do not let config override explicitly-set options
+  * tests: cover config-vs-app-set option precedence
+  * Add patches:
+    - libssh-options-do-not-let-config-override-explicitly-set-options.patch
+    - libssh-tests-cover-config-vs-app-set-option-precedence.patch
+
+-------------------------------------------------------------------

New:
----
  libssh-options-do-not-let-config-override-explicitly-set-options.patch
  libssh-tests-cover-config-vs-app-set-option-precedence.patch

----------(New B)----------
  New:  * Add patches:
    - libssh-options-do-not-let-config-override-explicitly-set-options.patch
    - libssh-tests-cover-config-vs-app-set-option-precedence.patch
  New:    - 
libssh-options-do-not-let-config-override-explicitly-set-options.patch
    - libssh-tests-cover-config-vs-app-set-option-precedence.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ libssh.spec ++++++
--- /var/tmp/diff_new_pack.1lfqZ6/_old  2026-09-23 14:35:23.852455078 +0200
+++ /var/tmp/diff_new_pack.1lfqZ6/_new  2026-09-23 14:35:23.855455202 +0200
@@ -48,6 +48,9 @@
 Patch1:         
libssh-tests-Fix-an-issue-where-torture_session-request-a-SIGTERM-too-early.patch
 # PATCH-FIX-UPSTREAM: /usr/etc support (bsc#1272547)
 Patch2:         libssh-cmake-Add-option-WITH_HERMETIC_USR.patch
+# PATCH-FIX-UPSTREAM: libssh ignores explicit username in URL if User 
specified in SSH config (bsc#1279934)
+Patch3:         
libssh-options-do-not-let-config-override-explicitly-set-options.patch
+Patch4:         libssh-tests-cover-config-vs-app-set-option-precedence.patch
 BuildRequires:  cmake
 BuildRequires:  gcc-c++
 BuildRequires:  krb5-devel


++++++ libssh-options-do-not-let-config-override-explicitly-set-options.patch 
++++++
>From f0af0190b5fdd8b5700435db8f5324bdd082b15a Mon Sep 17 00:00:00 2001
From: Shreyas Mahajan <[email protected]>
Date: Thu, 9 Jul 2026 00:20:08 +0530
Subject: [PATCH] options: do not let config override explicitly-set options

Signed-off-by: Shreyas Mahajan <[email protected]>
Reviewed-by: Jakub Jelen <[email protected]>
Merge-Request: <https://gitlab.com/libssh/libssh-mirror/-/merge_requests/844>
(cherry picked from commit af9977f855efbf0c2f59f7c61ec6bf91535a2cac)
---
 include/libssh/misc.h            |   5 ++
 src/config.c                     |   6 +-
 src/options.c                    | 143 ++++++++++++++++++++++++++++++-
 src/socket.c                     |  17 +++-
 tests/unittests/torture_config.c |   4 +-
 5 files changed, 167 insertions(+), 8 deletions(-)

diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index 8eab94ee5..cde6d8a5b 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -70,6 +70,11 @@ struct ssh_iterator {
 struct ssh_jump_info_struct {
     char *hostname;
     char *username;
+    /**
+     * Port number of the jump host, in the range 1-65535. Zero means the
+     * ProxyJump specification did not give a port, in which case the jump
+     * host's own configuration (or the connection default) supplies it.
+     */
     int port;
 };
 
diff --git a/src/config.c b/src/config.c
index cdc26b70c..4d3dcc38b 100644
--- a/src/config.c
+++ b/src/config.c
@@ -500,9 +500,9 @@ ssh_config_parse_proxy_jump(ssh_session session, const char 
*s, bool do_parsing)
                 SAFE_FREE(jump_host);
                 goto out;
             }
-            if (port == NULL) {
-                jump_host->port = 22;
-            } else {
+            /* Leave the port at 0 when it is not given, so that the jump
+             * host's own configuration can supply it later. */
+            if (port != NULL) {
                 jump_host->port = strtol(port, NULL, 10);
                 SAFE_FREE(port);
             }
diff --git a/src/options.c b/src/options.c
index 920c32eff..89e1d6b69 100644
--- a/src/options.c
+++ b/src/options.c
@@ -302,6 +302,129 @@ int ssh_options_set_algo(ssh_session session,
     return 0;
 }
 
+/*
+ * Map a public ssh_options_e onto the internal config opcode whose parser
+ * case applies it via ssh_options_set(). Used to mark a value as "seen" when
+ * an application sets it explicitly, so later config-file processing does not
+ * override it (OpenSSH "first obtained value wins" semantics).
+ *
+ * Returns SOC_UNKNOWN for options that must NOT be protected:
+ *   - accumulative options (IdentityFile/CertificateFile and friends),
+ *   - SSH_OPTIONS_HOST, the Host/Match lookup key that config HostName
+ *     intentionally overrides during alias resolution,
+ *   - operational settings such as log verbosity,
+ *   - options that have no ssh_config equivalent,
+ *   - getter-only options, which ssh_options_set() never accepts.
+ */
+static enum ssh_config_opcode_e ssh_opt_type_to_opcode(enum ssh_options_e type)
+{
+    switch (type) {
+    case SSH_OPTIONS_PORT:
+    case SSH_OPTIONS_PORT_STR:
+        return SOC_PORT;
+    case SSH_OPTIONS_USER:
+        return SOC_USERNAME;
+    case SSH_OPTIONS_KNOWNHOSTS:
+        return SOC_KNOWNHOSTS;
+    case SSH_OPTIONS_GLOBAL_KNOWNHOSTS:
+        return SOC_GLOBALKNOWNHOSTSFILE;
+    case SSH_OPTIONS_TIMEOUT:
+        return SOC_TIMEOUT;
+    case SSH_OPTIONS_CIPHERS_C_S:
+    case SSH_OPTIONS_CIPHERS_S_C:
+        return SOC_CIPHERS;
+    case SSH_OPTIONS_COMPRESSION:
+    case SSH_OPTIONS_COMPRESSION_C_S:
+    case SSH_OPTIONS_COMPRESSION_S_C:
+        return SOC_COMPRESSION;
+    case SSH_OPTIONS_PROXYCOMMAND:
+        return SOC_PROXYCOMMAND;
+    case SSH_OPTIONS_PROXYJUMP:
+        return SOC_PROXYJUMP;
+    case SSH_OPTIONS_BINDADDR:
+        return SOC_BINDADDRESS;
+    case SSH_OPTIONS_STRICTHOSTKEYCHECK:
+        return SOC_STRICTHOSTKEYCHECK;
+    case SSH_OPTIONS_KEY_EXCHANGE:
+        return SOC_KEXALGORITHMS;
+    case SSH_OPTIONS_HOSTKEYS:
+        return SOC_HOSTKEYALGORITHMS;
+    case SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES:
+        return SOC_PUBKEYACCEPTEDKEYTYPES;
+    case SSH_OPTIONS_HMAC_C_S:
+    case SSH_OPTIONS_HMAC_S_C:
+        return SOC_MACS;
+    case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY:
+        return SOC_GSSAPISERVERIDENTITY;
+    case SSH_OPTIONS_GSSAPI_CLIENT_IDENTITY:
+        return SOC_GSSAPICLIENTIDENTITY;
+    case SSH_OPTIONS_GSSAPI_DELEGATE_CREDENTIALS:
+        return SOC_GSSAPIDELEGATECREDENTIALS;
+    case SSH_OPTIONS_PASSWORD_AUTH:
+        return SOC_PASSWORDAUTHENTICATION;
+    case SSH_OPTIONS_PUBKEY_AUTH:
+        return SOC_PUBKEYAUTHENTICATION;
+    case SSH_OPTIONS_KBDINT_AUTH:
+        return SOC_KBDINTERACTIVEAUTHENTICATION;
+    case SSH_OPTIONS_GSSAPI_AUTH:
+        return SOC_GSSAPIAUTHENTICATION;
+    case SSH_OPTIONS_REKEY_DATA:
+    case SSH_OPTIONS_REKEY_TIME:
+        return SOC_REKEYLIMIT;
+    case SSH_OPTIONS_IDENTITY_AGENT:
+        return SOC_IDENTITYAGENT;
+    case SSH_OPTIONS_IDENTITIES_ONLY:
+        return SOC_IDENTITIESONLY;
+    case SSH_OPTIONS_CONTROL_MASTER:
+        return SOC_CONTROLMASTER;
+    case SSH_OPTIONS_CONTROL_PATH:
+        return SOC_CONTROLPATH;
+    /*
+     * Accumulative options append to a list instead of replacing a value, so
+     * the "first value wins" precedence between config and the application 
does
+     * not apply to them.
+     */
+    case SSH_OPTIONS_IDENTITY:
+    case SSH_OPTIONS_ADD_IDENTITY:
+    case SSH_OPTIONS_CERTIFICATE:
+    case SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND:
+    /*
+     * SSH_OPTIONS_HOST carries the destination as given by the application,
+     * which is OpenSSH's "host" (the Host/Match lookup key), not its
+     * "hostname". Config HostName resolves that key to the real hostname and
+     * must keep doing so. HostName has its own "first value wins" precedence
+     * between config entries, enforced independently via seen[SOC_HOSTNAME]
+     * while parsing the configuration.
+     */
+    case SSH_OPTIONS_HOST:
+    /*
+     * Operational settings that applications and frameworks routinely set on
+     * their own, independent of the connection configuration. OpenSSH's config
+     * parser notably does not let a previously-set value suppress LogLevel, so
+     * we follow it and leave log verbosity unprotected.
+     */
+    case SSH_OPTIONS_LOG_VERBOSITY:
+    case SSH_OPTIONS_LOG_VERBOSITY_STR:
+    /*
+     * Options with no OpenSSH ssh_config equivalent (or that are never applied
+     * from a config file), so there is no config value that could override the
+     * application's choice.
+     */
+    case SSH_OPTIONS_FD:
+    case SSH_OPTIONS_SSH_DIR:
+    case SSH_OPTIONS_SSH1:
+    case SSH_OPTIONS_SSH2:
+    case SSH_OPTIONS_TIMEOUT_USEC:
+    case SSH_OPTIONS_COMPRESSION_LEVEL:
+    case SSH_OPTIONS_NODELAY:
+    case SSH_OPTIONS_PROCESS_CONFIG:
+    case SSH_OPTIONS_RSA_MIN_SIZE:
+        return SOC_UNKNOWN;
+    }
+
+    return SOC_UNKNOWN;
+}
+
 /**
  * @brief This function can set all possible ssh options.
  *
@@ -659,6 +782,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e 
type,
     int rc;
     char **wanted_methods = session->opts.wanted_methods;
     struct ssh_jump_callbacks_struct *j = NULL;
+    enum ssh_config_opcode_e opcode;
 
     if (session == NULL) {
         return -1;
@@ -1360,6 +1484,17 @@ int ssh_options_set(ssh_session session, enum 
ssh_options_e type,
             break;
     }
 
+    /*
+     * The option was set successfully. Mark config-backed options as
+     * explicitly set so that later processing of OpenSSH configuration files
+     * keeps the application's value (issue #365). Options that map to
+     * SOC_UNKNOWN are intentionally left unmarked.
+     */
+    opcode = ssh_opt_type_to_opcode(type);
+    if (opcode != SOC_UNKNOWN) {
+        session->opts.options_seen[opcode] = 1;
+    }
+
     return 0;
 }
 
@@ -1806,8 +1941,12 @@ int ssh_options_getopt(ssh_session session, int 
*argcptr, char **argv)
 /**
  * @brief Parse the ssh config file.
  *
- * This should be the last call of all options, it may overwrite options which
- * are already set. It requires that the host name is already set with
+ * This should be the last call of all options. Options that were already set
+ * explicitly via ssh_options_set() take precedence and are not overwritten by
+ * the configuration file, matching OpenSSH's "first obtained value wins"
+ * behavior. Accumulative options such as IdentityFile and CertificateFile, as
+ * well as host-alias resolution via HostName, are still applied from the
+ * configuration. It requires that the host name is already set with
  * ssh_options_set(SSH_OPTIONS_HOST).
  *
  * @param  session      SSH session handle
diff --git a/src/socket.c b/src/socket.c
index 15ee6ab67..f15ea4456 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -1100,8 +1100,21 @@ jump_thread_func(void *arg)
     }
 
     ssh_options_set(jump_session, SSH_OPTIONS_HOST, jis->hostname);
-    ssh_options_set(jump_session, SSH_OPTIONS_USER, jis->username);
-    ssh_options_set(jump_session, SSH_OPTIONS_PORT, &jis->port);
+
+    /*
+     * Only propagate the username and port that the ProxyJump specification
+     * actually provided. Setting them unconditionally would inject internal
+     * defaults (the local username and port 22) as if the application had
+     * chosen them, which then prevents the jump host's own configuration from
+     * supplying these values (issue #365). When they are omitted here, the 
jump
+     * host config and the connection internals fill them in.
+     */
+    if (jis->username != NULL) {
+        ssh_options_set(jump_session, SSH_OPTIONS_USER, jis->username);
+    }
+    if (jis->port > 0) {
+        ssh_options_set(jump_session, SSH_OPTIONS_PORT, &jis->port);
+    }
 
     /* Pop the callbacks for the current jump */
     cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 6f0c447fd..65467302b 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -762,7 +762,9 @@ helper_proxy_jump_check(struct ssh_iterator *jump,
         int iport = strtol(port, NULL, 10);
         assert_int_equal(jis->port, iport);
     } else {
-        assert_int_equal(jis->port, 22);
+        /* No port in the ProxyJump spec: left unset for the jump host's own
+         * configuration to supply. */
+        assert_int_equal(jis->port, 0);
     }
 }
 
-- 
GitLab


++++++ libssh-tests-cover-config-vs-app-set-option-precedence.patch ++++++
>From 3e0d16c89b83bdc9c0fba0c43c845cc1eb33803c Mon Sep 17 00:00:00 2001
From: Shreyas Mahajan <[email protected]>
Date: Thu, 9 Jul 2026 11:02:49 +0530
Subject: [PATCH] tests: cover config-vs-app-set option precedence

Signed-off-by: Shreyas Mahajan <[email protected]>
Reviewed-by: Jakub Jelen <[email protected]>
Merge-Request: <https://gitlab.com/libssh/libssh-mirror/-/merge_requests/844>
(cherry picked from 1de573cfb5fb731184e0aaaf1d5a288d79877aef)
---
 tests/unittests/torture_config.c | 140 +++++++++++++++++++++++++++++++
 1 file changed, 140 insertions(+)

Index: libssh-0.11.5/tests/unittests/torture_config.c
===================================================================
--- libssh-0.11.5.orig/tests/unittests/torture_config.c
+++ libssh-0.11.5/tests/unittests/torture_config.c
@@ -2830,6 +2830,128 @@ static void torture_config_invalid(void
 #endif
 }
 
+/* Issue #365: a value set via ssh_options_set() before config parsing must
+ * NOT be overridden by the config file. */
+static void torture_config_user_not_overridden(void **state)
+{
+    ssh_session session = *state;
+    char *user = NULL;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_USER, "appuser");
+    assert_ssh_return_code(session, rc);
+
+    _parse_config(session, NULL, "User configuser\n", SSH_OK);
+
+    rc = ssh_options_get(session, SSH_OPTIONS_USER, &user);
+    assert_ssh_return_code(session, rc);
+    assert_non_null(user);
+    assert_string_equal(user, "appuser");
+    SSH_STRING_FREE_CHAR(user);
+}
+
+/* When the application did NOT set User, the config value still applies. */
+static void torture_config_user_from_config_applies(void **state)
+{
+    ssh_session session = *state;
+    char *user = NULL;
+    int rc;
+
+    _parse_config(session, NULL, "User configuser\n", SSH_OK);
+
+    rc = ssh_options_get(session, SSH_OPTIONS_USER, &user);
+    assert_ssh_return_code(session, rc);
+    assert_non_null(user);
+    assert_string_equal(user, "configuser");
+    SSH_STRING_FREE_CHAR(user);
+}
+
+/* Protection is general, not User-specific: an app-set Port survives config. 
*/
+static void torture_config_port_not_overridden(void **state)
+{
+    ssh_session session = *state;
+    unsigned int port = 2020;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_PORT, &port);
+    assert_ssh_return_code(session, rc);
+
+    _parse_config(session, NULL, "Port 2222\n", SSH_OK);
+    assert_int_equal(session->opts.port, 2020);
+}
+
+/* The host match key is NOT protected: config HostName still resolves an
+ * app-set alias to the real hostname. */
+static void torture_config_hostname_still_resolves(void **state)
+{
+    ssh_session session = *state;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_HOST, "myalias");
+    assert_ssh_return_code(session, rc);
+
+    _parse_config(session,
+                  NULL,
+                  "Host myalias\n\tHostName real.example.com\n",
+                  SSH_OK);
+
+    assert_non_null(session->opts.host);
+    assert_string_equal(session->opts.host, "real.example.com");
+}
+
+/* HostName keeps its own "first obtained value wins" precedence between config
+ * entries, independently of the application-set SSH_OPTIONS_HOST lookup key.
+ * Every target below resolves to the first HostName, matching OpenSSH:
+ *
+ *   $ ssh -F config -G test  | grep ^hostname   ->  hostname test
+ *   $ ssh -F config -G test2 | grep ^hostname   ->  hostname test
+ *   $ ssh -F config -G test3 | grep ^hostname   ->  hostname test
+ */
+static void torture_config_hostname_first_wins(void **state)
+{
+    ssh_session session = *state;
+    const char *config = "HostName test\n"
+                         "HostName test2\n"
+                         "Match host test2\n"
+                         "\tHostName test3\n";
+    const char *targets[] = {"test", "test2", "test3"};
+    size_t i;
+    int rc;
+
+    (void)session;
+
+    for (i = 0; i < ARRAY_SIZE(targets); i++) {
+        ssh_session s = ssh_new();
+        assert_non_null(s);
+
+        rc = ssh_options_set(s, SSH_OPTIONS_HOST, targets[i]);
+        assert_ssh_return_code(s, rc);
+
+        _parse_config(s, NULL, config, SSH_OK);
+
+        assert_non_null(s->opts.host);
+        assert_string_equal(s->opts.host, "test");
+
+        ssh_free(s);
+    }
+}
+
+/* Operational options like log verbosity are NOT protected: config LogLevel
+ * still applies even if the application set verbosity beforehand. */
+static void torture_config_loglevel_not_overridden(void **state)
+{
+    ssh_session session = *state;
+    int level = SSH_LOG_NOLOG;
+    int rc;
+
+    rc = ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &level);
+    assert_ssh_return_code(session, rc);
+
+    _parse_config(session, NULL, "LogLevel DEBUG3\n", SSH_OK);
+
+    assert_int_equal(session->common.log_verbosity, SSH_LOG_TRACE);
+}
+
 int torture_run_tests(void)
 {
     int rc;
@@ -2935,6 +3057,24 @@ int torture_run_tests(void)
         cmocka_unit_test_setup_teardown(torture_config_invalid,
                                         setup,
                                         teardown),
+        cmocka_unit_test_setup_teardown(torture_config_user_not_overridden,
+                                        setup,
+                                        teardown),
+        
cmocka_unit_test_setup_teardown(torture_config_user_from_config_applies,
+                                        setup,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_config_port_not_overridden,
+                                        setup,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_config_hostname_still_resolves,
+                                        setup,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_config_hostname_first_wins,
+                                        setup,
+                                        teardown),
+        cmocka_unit_test_setup_teardown(torture_config_loglevel_not_overridden,
+                                        setup,
+                                        teardown),
     };
 
 

Reply via email to