Skip to content

Commit 26dcf6e

Browse files
Ilyes Ben Hamoudamichalvasko
authored andcommitted
session BUGFIX do not close SSH socket twice
Since libssh 0.10, ssh_disconnect() only closes the socket only if it is not passed via options. For session server, the SSH socket is not passed to libssh via options. It is then closed twice in nc_session_free_transport(): once by libssh in ssh_disconnect() and again by us. This leads to undefined behaviour in multi-threaded programs. Currently, we can not distinguish between a socket passed via options and a socket owned by libssh for cleanup. There is no API in libssh that can retrieve the socket FD passed via options. Ensure to pass all sockets via options and handle ssh_disconnect() behavior before libssh 0.10 (libnetconf2 requires libssh >= 0.9.5) to prevent double close. Fixes: 70e9062 ("session BUGFIX close SSH socket") Signed-off-by: Ilyes Ben Hamouda <[email protected]>
1 parent 147b9a1 commit 26dcf6e

File tree

2 files changed

+26
-3
lines changed

2 files changed

+26
-3
lines changed

src/session.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -816,12 +816,18 @@ nc_session_free_transport(struct nc_session *session, int *multisession)
816816
free(siter);
817817
} while (session->ti.libssh.next != session);
818818
}
819-
/* remember sock so we can close it */
820-
sock = ssh_get_fd(session->ti.libssh.session);
821819
if (connected) {
822-
/* does not close sock */
820+
/* remember sock so we can close it */
821+
sock = ssh_get_fd(session->ti.libssh.session);
822+
823+
/* clears sock but does not close it if passed via options (libssh >= 0.10) */
823824
ssh_disconnect(session->ti.libssh.session);
825+
#if (LIBSSH_VERSION_MAJOR == 0 && LIBSSH_VERSION_MINOR < 10)
826+
sock = -1;
827+
#endif
824828
}
829+
830+
/* closes sock if set */
825831
ssh_free(session->ti.libssh.session);
826832
} else {
827833
/* remove the session from the list */

src/session_server_ssh.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2020,8 +2020,25 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt
20202020
if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
20212021
ERR(session, "SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
20222022
rc = -1;
2023+
2024+
/* Avoid closing the socket on failure to prevent a possible double close.
2025+
* On failure, sock may or not be set to the session. In theory, we should
2026+
* be able to compare sock with ssh_get_fd() and close it only if it was
2027+
* not set, for example:
2028+
*
2029+
* if (ssh_get_fd(session) == sock)
2030+
* sock = -1;
2031+
*
2032+
* However, if ssh_bind_accept_fd() fails to allocate the socket structure
2033+
* internally, calling ssh_get_fd() will dereference a NULL pointer due to
2034+
* a buggy behavior in libssh.
2035+
*/
2036+
sock = -1;
20232037
goto cleanup;
20242038
}
2039+
2040+
/* use SSH_OPTIONS_FD so libssh won't close the socket in ssh_disconnect() */
2041+
ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
20252042
sock = -1;
20262043

20272044
/* set to non-blocking */

0 commit comments

Comments
 (0)