Add support for logging "exit-signal", why not.
authorjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Mon, 18 Oct 2004 00:41:48 +0000 (00:41 +0000)
committerjacob <jacob@cda61777-01e9-0310-a592-d414129be87e>
Mon, 18 Oct 2004 00:41:48 +0000 (00:41 +0000)
This is disgustingly huge because old versions of OpenSSH got the message
format wrong, so we have to infer which format is in use. Tested with
Debian stable OpenSSH (3.4p1), with `uint32' packet, and lshd, which uses
the correct `string' packet, and also let me test "core dumped" and the
explanatory message.

git-svn-id: svn://svn.tartarus.org/sgt/putty@4653 cda61777-01e9-0310-a592-d414129be87e

ssh.c

diff --git a/ssh.c b/ssh.c
index c127fc5..268fc04 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -6354,6 +6354,7 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                unsigned localid;
                char *type;
                int typelen, want_reply;
+               int reply = SSH2_MSG_CHANNEL_FAILURE; /* default */
                struct ssh_channel *c;
 
                localid = ssh_pkt_getuint32(ssh);
@@ -6385,18 +6386,93 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                 * the request type string to see if it's something
                 * we recognise.
                 */
-               if (typelen == 11 && !memcmp(type, "exit-status", 11) &&
-                   c == ssh->mainchan) {
-                   /* We recognise "exit-status" on the primary channel. */
-                   char buf[100];
-                   ssh->exitcode = ssh_pkt_getuint32(ssh);
-                   sprintf(buf, "Server sent command exit status %d",
-                           ssh->exitcode);
-                   logevent(buf);
-                   if (want_reply) {
-                       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_SUCCESS);
-                       ssh2_pkt_adduint32(ssh, c->remoteid);
-                       ssh2_pkt_send(ssh);
+               if (c == ssh->mainchan) {
+                   /*
+                    * We recognise "exit-status" and "exit-signal" on
+                    * the primary channel.
+                    */
+                   if (typelen == 11 &&
+                       !memcmp(type, "exit-status", 11)) {
+
+                       ssh->exitcode = ssh_pkt_getuint32(ssh);
+                       logeventf(ssh, "Server sent command exit status %d",
+                                 ssh->exitcode);
+                       reply = SSH2_MSG_CHANNEL_SUCCESS;
+
+                   } else if (typelen == 11 &&
+                              !memcmp(type, "exit-signal", 11)) {
+
+                       int is_plausible = TRUE, is_int = FALSE;
+                       char *fmt_sig = "", *fmt_msg = "";
+                       char *msg;
+                       int msglen = 0, core = FALSE;
+                       /* ICK: older versions of OpenSSH (e.g. 3.4p1)
+                        * provide an `int' for the signal, despite its
+                        * having been a `string' in the drafts since at
+                        * least 2001. (Fixed in session.c 1.147.) Try to
+                        * infer which we can safely parse it as. */
+                       {
+                           unsigned char *p = ssh->pktin.body +
+                                              ssh->pktin.savedpos;
+                           long len = ssh->pktin.length - ssh->pktin.savedpos;
+                           unsigned long num = GET_32BIT(p); /* what is it? */
+                           /* If it's 0, it hardly matters; assume string */
+                           if (num == 0) {
+                               is_int = FALSE;
+                           } else {
+                               int maybe_int = FALSE, maybe_str = FALSE;
+#define CHECK_HYPOTHESIS(offset, result) \
+    do { \
+       long q = offset; \
+       if (q+4 <= len) { \
+           q = q + 4 + GET_32BIT(p+q); \
+           if (q+4 <= len && (q = q + 4 + GET_32BIT(p+q)) && q == len) \
+               result = TRUE; \
+       } \
+    } while(0)
+                               CHECK_HYPOTHESIS(4+1, maybe_int);
+                               CHECK_HYPOTHESIS(4+num+1, maybe_str);
+#undef CHECK_HYPOTHESIS
+                               if (maybe_int && !maybe_str)
+                                   is_int = TRUE;
+                               else if (!maybe_int && maybe_str)
+                                   is_int = FALSE;
+                               else
+                                   /* Crikey. Either or neither. Panic. */
+                                   is_plausible = FALSE;
+                           }
+                       }
+                       if (is_plausible) {
+                           if (is_int) {
+                               /* Old non-standard OpenSSH. */
+                               int signum = ssh_pkt_getuint32(ssh);
+                               fmt_sig = dupprintf(" %d", signum);
+                           } else {
+                               /* As per the drafts. */
+                               char *sig;
+                               int siglen;
+                               ssh_pkt_getstring(ssh, &sig, &siglen);
+                               /* Signal name isn't supposed to be blank, but
+                                * let's cope gracefully if it is. */
+                               if (siglen) {
+                                   fmt_sig = dupprintf(" \"%.*s\"",
+                                                       siglen, sig);
+                               }
+                           }
+                           core = ssh2_pkt_getbool(ssh);
+                           ssh_pkt_getstring(ssh, &msg, &msglen);
+                           if (msglen) {
+                               fmt_msg = dupprintf(" (\"%.*s\")", msglen, msg);
+                           }
+                           /* ignore lang tag */
+                       } /* else don't attempt to parse */
+                       logeventf(ssh, "Server exited on signal%s%s%s",
+                                 fmt_sig, core ? " (core dumped)" : "",
+                                 fmt_msg);
+                       if (*fmt_sig) sfree(fmt_sig);
+                       if (*fmt_msg) sfree(fmt_msg);
+                       reply = SSH2_MSG_CHANNEL_SUCCESS;
+
                    }
                } else {
                    /*
@@ -6405,11 +6481,12 @@ static void do_ssh2_authconn(Ssh ssh, unsigned char *in, int inlen, int ispkt)
                     * or respond with CHANNEL_FAILURE, depending
                     * on want_reply.
                     */
-                   if (want_reply) {
-                       ssh2_pkt_init(ssh, SSH2_MSG_CHANNEL_FAILURE);
-                       ssh2_pkt_adduint32(ssh, c->remoteid);
-                       ssh2_pkt_send(ssh);
-                   }
+                   reply = SSH2_MSG_CHANNEL_FAILURE;
+               }
+               if (want_reply) {
+                   ssh2_pkt_init(ssh, reply);
+                   ssh2_pkt_adduint32(ssh, c->remoteid);
+                   ssh2_pkt_send(ssh);
                }
            } else if (ssh->pktin.type == SSH2_MSG_GLOBAL_REQUEST) {
                char *type;