From: dtucker@openbsd.org Date: Fri, 5 Dec 2025 06:16:27 +0000 (+0000) Subject: upstream: Add an ssh -Oconninfo command X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eb97fc2b5e7c85a37fdb3f8a6ee1d665ef086c3f;p=thirdparty%2Fopenssh-portable.git upstream: Add an ssh -Oconninfo command that shows connection information, similar to the ~I escapechar. This is the first use of the mux extension mechanism, so it should be both forward and backward compatible: a new client talking to an old server will not allow the "conninfo" request to be sent, but everything else should work seamlessly. feedback and ok djm@ OpenBSD-Commit-ID: 50f047a85da277360558cabdfed59cb66f754341 --- diff --git a/clientloop.h b/clientloop.h index 4bc7bcd7c..1f550b35c 100644 --- a/clientloop.h +++ b/clientloop.h @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.h,v 1.38 2024/05/17 06:42:04 jsg Exp $ */ +/* $OpenBSD: clientloop.h,v 1.39 2025/12/05 06:16:27 dtucker Exp $ */ /* * Author: Tatu Ylonen @@ -75,6 +75,7 @@ void client_expect_confirm(struct ssh *, int, const char *, #define SSHMUX_COMMAND_STOP 6 /* Disable mux but not conn */ #define SSHMUX_COMMAND_CANCEL_FWD 7 /* Cancel forwarding(s) */ #define SSHMUX_COMMAND_PROXY 8 /* Open new connection */ +#define SSHMUX_COMMAND_CONNINFO 9 /* Show connection information */ void muxserver_listen(struct ssh *); int muxclient(const char *); diff --git a/mux.c b/mux.c index 37bcb9103..53cbab0fc 100644 --- a/mux.c +++ b/mux.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mux.c,v 1.107 2025/09/30 00:03:09 djm Exp $ */ +/* $OpenBSD: mux.c,v 1.108 2025/12/05 06:16:27 dtucker Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * @@ -122,6 +122,7 @@ struct mux_master_state { #define MUX_C_NEW_STDIO_FWD 0x10000008 #define MUX_C_STOP_LISTENING 0x10000009 #define MUX_C_PROXY 0x1000000f +#define MUX_C_EXT_INFO 0x20000001 #define MUX_S_OK 0x80000001 #define MUX_S_PERMISSION_DENIED 0x80000002 #define MUX_S_FAILURE 0x80000003 @@ -131,12 +132,18 @@ struct mux_master_state { #define MUX_S_REMOTE_PORT 0x80000007 #define MUX_S_TTY_ALLOC_FAIL 0x80000008 #define MUX_S_PROXY 0x8000000f +#define MUX_S_EXT_INFO 0x90000001 /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ #define MUX_FWD_LOCAL 1 #define MUX_FWD_REMOTE 2 #define MUX_FWD_DYNAMIC 3 +#define MUX_EXT_INFO 0x00000001 + +/* Bitmask of supported extensions */ +static u_int extensions = 0; + static void mux_session_confirm(struct ssh *, int, int, void *); static void mux_stdio_confirm(struct ssh *, int, int, void *); @@ -158,6 +165,8 @@ static int mux_master_process_stop_listening(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); static int mux_master_process_proxy(struct ssh *, u_int, Channel *, struct sshbuf *, struct sshbuf *); +static int mux_master_process_ext_info(struct ssh *, u_int, + Channel *, struct sshbuf *, struct sshbuf *); static const struct { u_int type; @@ -173,6 +182,7 @@ static const struct { { MUX_C_NEW_STDIO_FWD, mux_master_process_stdio_fwd }, { MUX_C_STOP_LISTENING, mux_master_process_stop_listening }, { MUX_C_PROXY, mux_master_process_proxy }, + { MUX_C_EXT_INFO, mux_master_process_ext_info }, { 0, NULL } }; @@ -287,8 +297,13 @@ mux_master_process_hello(struct ssh *ssh, u_int rid, error_fr(r, "parse extension"); return -1; } - debug2_f("Unrecognised extension \"%s\" length %zu", - name, value_len); + if (strcmp(name, "info") == 0) { + debug_f("Received 'info' extension"); + extensions |= MUX_EXT_INFO; + } else { + debug2_f("Unrecognised extension \"%s\" length %zu", + name, value_len); + } free(name); } state->hello_rcvd = 1; @@ -494,6 +509,39 @@ mux_master_process_alive_check(struct ssh *ssh, u_int rid, return 0; } +/* The "info" extension. */ +static int +mux_master_process_ext_info(struct ssh *ssh, u_int rid, + Channel *c, struct sshbuf *m, struct sshbuf *reply) +{ + int r; + u_int status = 0; + char *name = NULL, *msg = NULL; + + debug2_f("channel %d: info request", c->self); + + if ((r = sshbuf_get_cstring(m, &name, NULL)) != 0) + fatal_fr(r, "parse"); + + if (strcmp(name, "connection") == 0) { + if ((msg = connection_info_message(ssh)) == NULL) + fatal_f("connection_info_message"); + status = 1; + } else { + msg = xstrdup("info request type not supported"); + } + + /* prepare reply */ + if ((r = sshbuf_put_u32(reply, MUX_S_EXT_INFO)) != 0 || + (r = sshbuf_put_u32(reply, rid)) != 0 || + (r = sshbuf_put_u32(reply, status)) != 0 || + (r = sshbuf_put_cstring(reply, msg)) != 0) + fatal_fr(r, "reply"); + free(msg); + + return 0; +} + static int mux_master_process_terminate(struct ssh *ssh, u_int rid, Channel *c, struct sshbuf *m, struct sshbuf *reply) @@ -1160,7 +1208,10 @@ mux_master_read_cb(struct ssh *ssh, Channel *c) if ((r = sshbuf_put_u32(out, MUX_MSG_HELLO)) != 0 || (r = sshbuf_put_u32(out, SSHMUX_VER)) != 0) fatal_fr(r, "reply"); - /* no extensions */ + /* "info" extension */ + if ((r = sshbuf_put_cstring(out, "info")) != 0 || + (r = sshbuf_put_cstring(out, "0")) != 0) + fatal_fr(r, "put info extension"); if ((r = sshbuf_put_stringb(c->output, out)) != 0) fatal_fr(r, "enqueue"); debug3_f("channel %d: hello sent", c->self); @@ -1635,7 +1686,13 @@ mux_client_hello_exchange(int fd, int timeout_ms) error_fr(r, "parse extension"); goto out; } - debug2("Unrecognised master extension \"%s\"", name); + /* Process extensions. */ + if (strcmp(name, "info") == 0) { + debug("Received 'info' extension"); + extensions |= MUX_EXT_INFO; + } else { + debug2("Unrecognised master extension \"%s\"", name); + } free(name); } /* success */ @@ -1696,6 +1753,57 @@ mux_client_request_alive(int fd) return pid; } +static char * +mux_client_request_info(int fd, const char *name) +{ + struct sshbuf *m; + char *e, *msg; + u_int type, rid, status; + int r; + + debug3_f("entering"); + + if ((m = sshbuf_new()) == NULL) + fatal_f("sshbuf_new"); + if ((r = sshbuf_put_u32(m, MUX_C_EXT_INFO)) != 0 || + (r = sshbuf_put_u32(m, muxclient_request_id)) != 0 || + (r = sshbuf_put_cstring(m, name)) != 0) + fatal_fr(r, "assemble"); + + if (mux_client_write_packet(fd, m) != 0) + fatal_f("write packet: %s", strerror(errno)); + + sshbuf_reset(m); + + /* Read their reply */ + if (mux_client_read_packet(fd, m) != 0) { + sshbuf_free(m); + return 0; + } + + if ((r = sshbuf_get_u32(m, &type)) != 0) + fatal_fr(r, "parse type"); + if (type != MUX_S_EXT_INFO) { + if ((r = sshbuf_get_cstring(m, &e, NULL)) != 0) + fatal_fr(r, "parse error message"); + fatal_f("master returned error: %s", e); + } + + if ((r = sshbuf_get_u32(m, &rid)) != 0) + fatal_fr(r, "parse remote ID"); + if (rid != muxclient_request_id) + fatal_f("out of sequence reply: my id %u theirs %u", + muxclient_request_id, rid); + if ((r = sshbuf_get_u32(m, &status)) != 0 || + (r = sshbuf_get_cstring(m, &msg, NULL)) != 0) + fatal_fr(r, "parse connection info"); + sshbuf_free(m); + + muxclient_request_id++; + + return msg; +} + static void mux_client_request_terminate(int fd) { @@ -2261,6 +2369,7 @@ muxclient(const char *path) struct sockaddr_un addr; int sock, timeout = options.connection_timeout, timeout_ms = -1; u_int pid; + char *conninfo = NULL; if (muxclient_command == 0) { if (options.stdio_forward_host != NULL) @@ -2331,6 +2440,14 @@ muxclient(const char *path) fatal_f("master alive check failed"); fprintf(stderr, "Master running (pid=%u)\r\n", pid); exit(0); + case SSHMUX_COMMAND_CONNINFO: + if (!(extensions & MUX_EXT_INFO)) + fatal("mux server does not support conninfo"); + conninfo = mux_client_request_info(sock, "connection"); + if (conninfo == NULL) + fatal_f("connection info request failed"); + printf("%s", conninfo); + exit(0); case SSHMUX_COMMAND_TERMINATE: mux_client_request_terminate(sock); if (options.log_level != SYSLOG_LEVEL_QUIET) diff --git a/packet.c b/packet.c index be90e90d4..2a5a56a88 100644 --- a/packet.c +++ b/packet.c @@ -1,4 +1,4 @@ -/* $OpenBSD: packet.c,v 1.326 2025/11/29 06:49:56 dtucker Exp $ */ +/* $OpenBSD: packet.c,v 1.327 2025/12/05 06:16:27 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -3038,7 +3038,7 @@ connection_info_message(struct ssh *ssh) } comp_info = comp_status_message(ssh); - xasprintf(&ret, "Connection information for %s pid %lld:\r\n" + xasprintf(&ret, "Connection information for %s pid %lld\r\n" "%s" " kexalgorithm %s\r\n hostkeyalgorithm %s\r\n" " cipher %s\r\n mac %s\r\n compression %s\r\n" diff --git a/ssh.1 b/ssh.1 index 391bcdbb6..ac218cc51 100644 --- a/ssh.1 +++ b/ssh.1 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh.1,v 1.445 2025/11/27 02:18:48 dtucker Exp $ -.Dd $Mdocdate: November 27 2025 $ +.\" $OpenBSD: ssh.1,v 1.446 2025/12/05 06:16:27 dtucker Exp $ +.Dd $Mdocdate: December 5 2025 $ .Dt SSH 1 .Os .Sh NAME @@ -486,6 +486,8 @@ argument is interpreted and passed to the master process. Valid commands are: .Dq check (check that the master process is running), +.Dq conninfo +(report information about the master connection), .Dq forward (request forwardings without command execution), .Dq cancel diff --git a/ssh.c b/ssh.c index df302c34b..461b60975 100644 --- a/ssh.c +++ b/ssh.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh.c,v 1.620 2025/11/13 10:35:14 dtucker Exp $ */ +/* $OpenBSD: ssh.c,v 1.621 2025/12/05 06:16:27 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -817,6 +817,8 @@ main(int ac, char **av) fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; + else if (strcmp(optarg, "conninfo") == 0) + muxclient_command = SSHMUX_COMMAND_CONNINFO; else if (strcmp(optarg, "forward") == 0) muxclient_command = SSHMUX_COMMAND_FORWARD; else if (strcmp(optarg, "exit") == 0)