]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
upstream: Add an ssh -Oconninfo command
authordtucker@openbsd.org <dtucker@openbsd.org>
Fri, 5 Dec 2025 06:16:27 +0000 (06:16 +0000)
committerDarren Tucker <dtucker@dtucker.net>
Fri, 5 Dec 2025 07:05:44 +0000 (18:05 +1100)
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

clientloop.h
mux.c
packet.c
ssh.1
ssh.c

index 4bc7bcd7c4f299b03a802a05232dfa2002e1b005..1f550b35cd1e9da94a18afc38283b886e4be9d5d 100644 (file)
@@ -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 <ylo@cs.hut.fi>
@@ -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 37bcb91037ef99fc27f8732a26dfc35a9978e5c6..53cbab0fc87dd271e1f4ff9518b387a206f6faa8 100644 (file)
--- 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 <djm@openbsd.org>
  *
@@ -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)
index be90e90d4d55162293b4d312e4eb17ade31b939c..2a5a56a886da0005c869d413ecace0d1572eb547 100644 (file)
--- 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 <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 391bcdbb61878aac989ee90fa15e13bfaad857a4..ac218cc513403f09c2835ed30a3e02b26ebd2300 100644 (file)
--- 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 df302c34ba98cdc2c95b082ed47a0d990aa9cd97..461b60975b1aac153918362a7397ddf11aeb70b0 100644 (file)
--- 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 <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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)