summaryrefslogtreecommitdiffstats
path: root/ttymodes.c
diff options
context:
space:
mode:
Diffstat (limited to 'ttymodes.c')
-rw-r--r--ttymodes.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/ttymodes.c b/ttymodes.c
new file mode 100644
index 0000000..1d20ce8
--- /dev/null
+++ b/ttymodes.c
@@ -0,0 +1,450 @@
+/* $OpenBSD: ttymodes.c,v 1.36 2021/01/27 09:26:54 djm Exp $ */
+/*
+ * Author: Tatu Ylonen <ylo@cs.hut.fi>
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ * All rights reserved
+ *
+ * As far as I am concerned, the code I have written for this software
+ * can be used freely for any purpose. Any derived versions of this
+ * software must be clearly marked as such, and if the derived work is
+ * incompatible with the protocol description in the RFC file, it must be
+ * called by a name other than "ssh" or "Secure Shell".
+ */
+
+/*
+ * SSH2 tty modes support by Kevin Steves.
+ * Copyright (c) 2001 Kevin Steves. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Encoding and decoding of terminal modes in a portable way.
+ * Much of the format is defined in ttymodes.h; it is included multiple times
+ * into this file with the appropriate macro definitions to generate the
+ * suitable code.
+ */
+
+#include "includes.h"
+
+#include <sys/types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <termios.h>
+#include <stdarg.h>
+
+#include "packet.h"
+#include "log.h"
+#include "compat.h"
+#include "sshbuf.h"
+#include "ssherr.h"
+
+#define TTY_OP_END 0
+/*
+ * uint32 (u_int) follows speed.
+ */
+#define TTY_OP_ISPEED 128
+#define TTY_OP_OSPEED 129
+
+/*
+ * Converts POSIX speed_t to a baud rate. The values of the
+ * constants for speed_t are not themselves portable.
+ */
+static int
+speed_to_baud(speed_t speed)
+{
+ switch (speed) {
+ case B0:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+
+#ifdef B19200
+ case B19200:
+ return 19200;
+#else /* B19200 */
+#ifdef EXTA
+ case EXTA:
+ return 19200;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+ case B38400:
+ return 38400;
+#else /* B38400 */
+#ifdef EXTB
+ case EXTB:
+ return 38400;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+ case B7200:
+ return 7200;
+#endif /* B7200 */
+#ifdef B14400
+ case B14400:
+ return 14400;
+#endif /* B14400 */
+#ifdef B28800
+ case B28800:
+ return 28800;
+#endif /* B28800 */
+#ifdef B57600
+ case B57600:
+ return 57600;
+#endif /* B57600 */
+#ifdef B76800
+ case B76800:
+ return 76800;
+#endif /* B76800 */
+#ifdef B115200
+ case B115200:
+ return 115200;
+#endif /* B115200 */
+#ifdef B230400
+ case B230400:
+ return 230400;
+#endif /* B230400 */
+ default:
+ return 9600;
+ }
+}
+
+/*
+ * Converts a numeric baud rate to a POSIX speed_t.
+ */
+static speed_t
+baud_to_speed(int baud)
+{
+ switch (baud) {
+ case 0:
+ return B0;
+ case 50:
+ return B50;
+ case 75:
+ return B75;
+ case 110:
+ return B110;
+ case 134:
+ return B134;
+ case 150:
+ return B150;
+ case 200:
+ return B200;
+ case 300:
+ return B300;
+ case 600:
+ return B600;
+ case 1200:
+ return B1200;
+ case 1800:
+ return B1800;
+ case 2400:
+ return B2400;
+ case 4800:
+ return B4800;
+ case 9600:
+ return B9600;
+
+#ifdef B19200
+ case 19200:
+ return B19200;
+#else /* B19200 */
+#ifdef EXTA
+ case 19200:
+ return EXTA;
+#endif /* EXTA */
+#endif /* B19200 */
+
+#ifdef B38400
+ case 38400:
+ return B38400;
+#else /* B38400 */
+#ifdef EXTB
+ case 38400:
+ return EXTB;
+#endif /* EXTB */
+#endif /* B38400 */
+
+#ifdef B7200
+ case 7200:
+ return B7200;
+#endif /* B7200 */
+#ifdef B14400
+ case 14400:
+ return B14400;
+#endif /* B14400 */
+#ifdef B28800
+ case 28800:
+ return B28800;
+#endif /* B28800 */
+#ifdef B57600
+ case 57600:
+ return B57600;
+#endif /* B57600 */
+#ifdef B76800
+ case 76800:
+ return B76800;
+#endif /* B76800 */
+#ifdef B115200
+ case 115200:
+ return B115200;
+#endif /* B115200 */
+#ifdef B230400
+ case 230400:
+ return B230400;
+#endif /* B230400 */
+ default:
+ return B9600;
+ }
+}
+
+/*
+ * Encode a special character into SSH line format.
+ */
+static u_int
+special_char_encode(cc_t c)
+{
+#ifdef _POSIX_VDISABLE
+ if (c == _POSIX_VDISABLE)
+ return 255;
+#endif /* _POSIX_VDISABLE */
+ return c;
+}
+
+/*
+ * Decode a special character from SSH line format.
+ */
+static cc_t
+special_char_decode(u_int c)
+{
+#ifdef _POSIX_VDISABLE
+ if (c == 255)
+ return _POSIX_VDISABLE;
+#endif /* _POSIX_VDISABLE */
+ return c;
+}
+
+/*
+ * Encodes terminal modes for the terminal referenced by fd
+ * or tiop in a portable manner, and appends the modes to a packet
+ * being constructed.
+ */
+void
+ssh_tty_make_modes(struct ssh *ssh, int fd, struct termios *tiop)
+{
+ struct termios tio;
+ struct sshbuf *buf;
+ int r, ibaud, obaud;
+
+ if ((buf = sshbuf_new()) == NULL)
+ fatal_f("sshbuf_new failed");
+
+ if (tiop == NULL) {
+ if (fd == -1) {
+ debug_f("no fd or tio");
+ goto end;
+ }
+ if (tcgetattr(fd, &tio) == -1) {
+ logit("tcgetattr: %.100s", strerror(errno));
+ goto end;
+ }
+ } else
+ tio = *tiop;
+
+ /* Store input and output baud rates. */
+ obaud = speed_to_baud(cfgetospeed(&tio));
+ ibaud = speed_to_baud(cfgetispeed(&tio));
+ if ((r = sshbuf_put_u8(buf, TTY_OP_OSPEED)) != 0 ||
+ (r = sshbuf_put_u32(buf, obaud)) != 0 ||
+ (r = sshbuf_put_u8(buf, TTY_OP_ISPEED)) != 0 ||
+ (r = sshbuf_put_u32(buf, ibaud)) != 0)
+ fatal_fr(r, "compose");
+
+ /* Store values of mode flags. */
+#define TTYCHAR(NAME, OP) \
+ if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
+ (r = sshbuf_put_u32(buf, \
+ special_char_encode(tio.c_cc[NAME]))) != 0) \
+ fatal_fr(r, "compose %s", #NAME);
+
+#define SSH_TTYMODE_IUTF8 42 /* for SSH_BUG_UTF8TTYMODE */
+
+#define TTYMODE(NAME, FIELD, OP) \
+ if (OP == SSH_TTYMODE_IUTF8 && (ssh->compat & SSH_BUG_UTF8TTYMODE)) { \
+ debug3_f("SSH_BUG_UTF8TTYMODE"); \
+ } else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
+ (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \
+ fatal_fr(r, "compose %s", #NAME);
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+
+end:
+ /* Mark end of mode data. */
+ if ((r = sshbuf_put_u8(buf, TTY_OP_END)) != 0 ||
+ (r = sshpkt_put_stringb(ssh, buf)) != 0)
+ fatal_fr(r, "compose end");
+ sshbuf_free(buf);
+}
+
+/*
+ * Decodes terminal modes for the terminal referenced by fd in a portable
+ * manner from a packet being read.
+ */
+void
+ssh_tty_parse_modes(struct ssh *ssh, int fd)
+{
+ struct termios tio;
+ struct sshbuf *buf;
+ const u_char *data;
+ u_char opcode;
+ u_int baud, u;
+ int r, failure = 0;
+ size_t len;
+
+ if ((r = sshpkt_get_string_direct(ssh, &data, &len)) != 0)
+ fatal_fr(r, "parse");
+ if (len == 0)
+ return;
+ if ((buf = sshbuf_from(data, len)) == NULL) {
+ error_f("sshbuf_from failed");
+ return;
+ }
+
+ /*
+ * Get old attributes for the terminal. We will modify these
+ * flags. I am hoping that if there are any machine-specific
+ * modes, they will initially have reasonable values.
+ */
+ if (tcgetattr(fd, &tio) == -1) {
+ logit("tcgetattr: %.100s", strerror(errno));
+ failure = -1;
+ }
+
+ while (sshbuf_len(buf) > 0) {
+ if ((r = sshbuf_get_u8(buf, &opcode)) != 0)
+ fatal_fr(r, "parse opcode");
+ switch (opcode) {
+ case TTY_OP_END:
+ goto set;
+
+ case TTY_OP_ISPEED:
+ if ((r = sshbuf_get_u32(buf, &baud)) != 0)
+ fatal_fr(r, "parse ispeed");
+ if (failure != -1 &&
+ cfsetispeed(&tio, baud_to_speed(baud)) == -1)
+ error("cfsetispeed failed for %d", baud);
+ break;
+
+ case TTY_OP_OSPEED:
+ if ((r = sshbuf_get_u32(buf, &baud)) != 0)
+ fatal_fr(r, "parse ospeed");
+ if (failure != -1 &&
+ cfsetospeed(&tio, baud_to_speed(baud)) == -1)
+ error("cfsetospeed failed for %d", baud);
+ break;
+
+#define TTYCHAR(NAME, OP) \
+ case OP: \
+ if ((r = sshbuf_get_u32(buf, &u)) != 0) \
+ fatal_fr(r, "parse %s", #NAME); \
+ tio.c_cc[NAME] = special_char_decode(u); \
+ break;
+#define TTYMODE(NAME, FIELD, OP) \
+ case OP: \
+ if ((r = sshbuf_get_u32(buf, &u)) != 0) \
+ fatal_fr(r, "parse %s", #NAME); \
+ if (u) \
+ tio.FIELD |= NAME; \
+ else \
+ tio.FIELD &= ~NAME; \
+ break;
+
+#include "ttymodes.h"
+
+#undef TTYCHAR
+#undef TTYMODE
+
+ default:
+ debug("Ignoring unsupported tty mode opcode %d (0x%x)",
+ opcode, opcode);
+ /*
+ * SSH2:
+ * Opcodes 1 to 159 are defined to have a uint32
+ * argument.
+ * Opcodes 160 to 255 are undefined and cause parsing
+ * to stop.
+ */
+ if (opcode > 0 && opcode < 160) {
+ if ((r = sshbuf_get_u32(buf, NULL)) != 0)
+ fatal_fr(r, "parse arg");
+ break;
+ } else {
+ logit_f("unknown opcode %d", opcode);
+ goto set;
+ }
+ }
+ }
+
+set:
+ len = sshbuf_len(buf);
+ sshbuf_free(buf);
+ if (len > 0) {
+ logit_f("%zu bytes left", len);
+ return; /* Don't process bytes passed */
+ }
+ if (failure == -1)
+ return; /* Packet parsed ok but tcgetattr() failed */
+
+ /* Set the new modes for the terminal. */
+ if (tcsetattr(fd, TCSANOW, &tio) == -1)
+ logit("Setting tty modes failed: %.100s", strerror(errno));
+}