123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457 |
- /* $OpenBSD: ttymodes.c,v 1.34 2018/07/09 21:20:26 markus 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("%s: sshbuf_new failed", __func__);
- if (tiop == NULL) {
- if (fd == -1) {
- debug("%s: no fd or tio", __func__);
- 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("%s: buffer error: %s", __func__, ssh_err(r));
- /* 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("%s: buffer error: %s", __func__, ssh_err(r)); \
- #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("%s: SSH_BUG_UTF8TTYMODE", __func__); \
- } else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
- (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \
- fatal("%s: buffer error: %s", __func__, ssh_err(r)); \
- #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("%s: packet error: %s", __func__, ssh_err(r));
- 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("%s: packet error: %s", __func__, ssh_err(r));
- if (len == 0)
- return;
- if ((buf = sshbuf_from(data, len)) == NULL) {
- error("%s: sshbuf_from failed", __func__);
- 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("%s: packet error: %s", __func__, ssh_err(r));
- switch (opcode) {
- case TTY_OP_END:
- goto set;
- case TTY_OP_ISPEED:
- if ((r = sshbuf_get_u32(buf, &baud)) != 0)
- fatal("%s: packet error: %s",
- __func__, ssh_err(r));
- 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("%s: packet error: %s",
- __func__, ssh_err(r));
- 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("%s: packet error: %s", __func__, \
- ssh_err(r)); \
- 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("%s: packet error: %s", __func__, \
- ssh_err(r)); \
- 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("%s: packet error: %s", __func__,
- ssh_err(r));
- break;
- } else {
- logit("%s: unknown opcode %d", __func__,
- opcode);
- goto set;
- }
- }
- }
- set:
- len = sshbuf_len(buf);
- sshbuf_free(buf);
- if (len > 0) {
- logit("%s: %zu bytes left", __func__, 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));
- }
|