ttymodes.c revision 181097
159138Smsmith/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */ 259138Smsmith/* 359138Smsmith * Author: Tatu Ylonen <ylo@cs.hut.fi> 459138Smsmith * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 559138Smsmith * All rights reserved 659138Smsmith * 759138Smsmith * As far as I am concerned, the code I have written for this software 859138Smsmith * can be used freely for any purpose. Any derived versions of this 959138Smsmith * software must be clearly marked as such, and if the derived work is 1059138Smsmith * incompatible with the protocol description in the RFC file, it must be 1159138Smsmith * called by a name other than "ssh" or "Secure Shell". 1259138Smsmith */ 1359138Smsmith 1459138Smsmith/* 1559138Smsmith * SSH2 tty modes support by Kevin Steves. 1659138Smsmith * Copyright (c) 2001 Kevin Steves. All rights reserved. 1759138Smsmith * 1859138Smsmith * Redistribution and use in source and binary forms, with or without 1959138Smsmith * modification, are permitted provided that the following conditions 2059138Smsmith * are met: 2159138Smsmith * 1. Redistributions of source code must retain the above copyright 2259138Smsmith * notice, this list of conditions and the following disclaimer. 2359138Smsmith * 2. Redistributions in binary form must reproduce the above copyright 2459138Smsmith * notice, this list of conditions and the following disclaimer in the 2559138Smsmith * documentation and/or other materials provided with the distribution. 2659138Smsmith * 2759138Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2859138Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2959138Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3059138Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3159138Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3259138Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3359138Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3459138Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3559138Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3659138Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3759159Smsmith */ 3859159Smsmith 3959138Smsmith/* 4059138Smsmith * Encoding and decoding of terminal modes in a portable way. 4159138Smsmith * Much of the format is defined in ttymodes.h; it is included multiple times 4259138Smsmith * into this file with the appropriate macro definitions to generate the 4359138Smsmith * suitable code. 4459138Smsmith */ 4559138Smsmith 4659138Smsmith#include "includes.h" 4759138Smsmith 4859138Smsmith#include <sys/types.h> 4959138Smsmith 5059138Smsmith#include <errno.h> 5159138Smsmith#include <string.h> 5259138Smsmith#include <termios.h> 5359138Smsmith#include <stdarg.h> 5459138Smsmith 5559138Smsmith#include "packet.h" 5659138Smsmith#include "log.h" 5759138Smsmith#include "ssh1.h" 5859138Smsmith#include "compat.h" 5959138Smsmith#include "buffer.h" 6059138Smsmith 6159138Smsmith#define TTY_OP_END 0 6259138Smsmith/* 6359138Smsmith * uint32 (u_int) follows speed in SSH1 and SSH2 6459138Smsmith */ 6559138Smsmith#define TTY_OP_ISPEED_PROTO1 192 6659138Smsmith#define TTY_OP_OSPEED_PROTO1 193 6759138Smsmith#define TTY_OP_ISPEED_PROTO2 128 6859138Smsmith#define TTY_OP_OSPEED_PROTO2 129 6959138Smsmith 7059138Smsmith/* 7159138Smsmith * Converts POSIX speed_t to a baud rate. The values of the 7259138Smsmith * constants for speed_t are not themselves portable. 7359138Smsmith */ 7459138Smsmithstatic int 7559138Smsmithspeed_to_baud(speed_t speed) 7659138Smsmith{ 7759138Smsmith switch (speed) { 7859138Smsmith case B0: 7959138Smsmith return 0; 8059138Smsmith case B50: 8159138Smsmith return 50; 8259138Smsmith case B75: 8359138Smsmith return 75; 8459138Smsmith case B110: 8559138Smsmith return 110; 8659138Smsmith case B134: 8759138Smsmith return 134; 8859138Smsmith case B150: 8959138Smsmith return 150; 9059138Smsmith case B200: 9159138Smsmith return 200; 9259138Smsmith case B300: 9359138Smsmith return 300; 9459138Smsmith case B600: 9559138Smsmith return 600; 9659138Smsmith case B1200: 9759138Smsmith return 1200; 9859138Smsmith case B1800: 9959138Smsmith return 1800; 10059138Smsmith case B2400: 10159138Smsmith return 2400; 10259138Smsmith case B4800: 10359138Smsmith return 4800; 10459138Smsmith case B9600: 10559138Smsmith return 9600; 10659138Smsmith 10759138Smsmith#ifdef B19200 10859138Smsmith case B19200: 10959138Smsmith return 19200; 11059138Smsmith#else /* B19200 */ 11159138Smsmith#ifdef EXTA 11259138Smsmith case EXTA: 11359138Smsmith return 19200; 11459138Smsmith#endif /* EXTA */ 11559138Smsmith#endif /* B19200 */ 11659138Smsmith 11759138Smsmith#ifdef B38400 11859138Smsmith case B38400: 11959138Smsmith return 38400; 12059138Smsmith#else /* B38400 */ 12159138Smsmith#ifdef EXTB 12259138Smsmith case EXTB: 12359138Smsmith return 38400; 12459138Smsmith#endif /* EXTB */ 12559138Smsmith#endif /* B38400 */ 12659138Smsmith 12759138Smsmith#ifdef B7200 12859138Smsmith case B7200: 12959138Smsmith return 7200; 13059138Smsmith#endif /* B7200 */ 13159138Smsmith#ifdef B14400 13259138Smsmith case B14400: 13359138Smsmith return 14400; 13459138Smsmith#endif /* B14400 */ 13559138Smsmith#ifdef B28800 13659138Smsmith case B28800: 13759138Smsmith return 28800; 13859138Smsmith#endif /* B28800 */ 13959138Smsmith#ifdef B57600 14059138Smsmith case B57600: 14159138Smsmith return 57600; 14259138Smsmith#endif /* B57600 */ 14359138Smsmith#ifdef B76800 14459138Smsmith case B76800: 14559138Smsmith return 76800; 14659138Smsmith#endif /* B76800 */ 14759138Smsmith#ifdef B115200 14859138Smsmith case B115200: 14959138Smsmith return 115200; 15059138Smsmith#endif /* B115200 */ 15159138Smsmith#ifdef B230400 15259138Smsmith case B230400: 15359138Smsmith return 230400; 15459138Smsmith#endif /* B230400 */ 15559138Smsmith default: 15659138Smsmith return 9600; 15759138Smsmith } 15859138Smsmith} 15959138Smsmith 16059138Smsmith/* 16159138Smsmith * Converts a numeric baud rate to a POSIX speed_t. 16259138Smsmith */ 16359138Smsmithstatic speed_t 16459138Smsmithbaud_to_speed(int baud) 16559138Smsmith{ 16659138Smsmith switch (baud) { 16759138Smsmith case 0: 16859138Smsmith return B0; 16959138Smsmith case 50: 17059138Smsmith return B50; 17159138Smsmith case 75: 17259138Smsmith return B75; 17359138Smsmith case 110: 17459138Smsmith return B110; 17559138Smsmith case 134: 17659138Smsmith return B134; 17759138Smsmith case 150: 17859138Smsmith return B150; 17959138Smsmith case 200: 18059138Smsmith return B200; 18159138Smsmith case 300: 18259138Smsmith return B300; 18359138Smsmith case 600: 18459138Smsmith return B600; 18559138Smsmith case 1200: 18659138Smsmith return B1200; 18759138Smsmith case 1800: 18859138Smsmith return B1800; 18959138Smsmith case 2400: 19059138Smsmith return B2400; 19159138Smsmith case 4800: 19259138Smsmith return B4800; 19359138Smsmith case 9600: 19459138Smsmith return B9600; 19559138Smsmith 19659138Smsmith#ifdef B19200 19759138Smsmith case 19200: 19859138Smsmith return B19200; 19959138Smsmith#else /* B19200 */ 20059138Smsmith#ifdef EXTA 20159138Smsmith case 19200: 20259138Smsmith return EXTA; 20359138Smsmith#endif /* EXTA */ 20459138Smsmith#endif /* B19200 */ 20559138Smsmith 20659138Smsmith#ifdef B38400 20759138Smsmith case 38400: 20859138Smsmith return B38400; 20959138Smsmith#else /* B38400 */ 21059138Smsmith#ifdef EXTB 21159138Smsmith case 38400: 21259138Smsmith return EXTB; 21359138Smsmith#endif /* EXTB */ 21459138Smsmith#endif /* B38400 */ 21559138Smsmith 21659138Smsmith#ifdef B7200 21759138Smsmith case 7200: 21859138Smsmith return B7200; 21959138Smsmith#endif /* B7200 */ 22059138Smsmith#ifdef B14400 22159138Smsmith case 14400: 22259138Smsmith return B14400; 22359138Smsmith#endif /* B14400 */ 22459138Smsmith#ifdef B28800 22559138Smsmith case 28800: 22659138Smsmith return B28800; 22759138Smsmith#endif /* B28800 */ 22859138Smsmith#ifdef B57600 22959138Smsmith case 57600: 23059138Smsmith return B57600; 23159138Smsmith#endif /* B57600 */ 23259138Smsmith#ifdef B76800 23359138Smsmith case 76800: 23459138Smsmith return B76800; 23559138Smsmith#endif /* B76800 */ 23659138Smsmith#ifdef B115200 23759138Smsmith case 115200: 23859138Smsmith return B115200; 23959138Smsmith#endif /* B115200 */ 24059138Smsmith#ifdef B230400 24159138Smsmith case 230400: 24259138Smsmith return B230400; 24359138Smsmith#endif /* B230400 */ 24459138Smsmith default: 24559138Smsmith return B9600; 24659138Smsmith } 24759138Smsmith} 24859138Smsmith 24959138Smsmith/* 25059138Smsmith * Encode a special character into SSH line format. 25159138Smsmith */ 25259138Smsmithstatic u_int 25359138Smsmithspecial_char_encode(cc_t c) 25459138Smsmith{ 25559138Smsmith#ifdef _POSIX_VDISABLE 25659138Smsmith if (c == _POSIX_VDISABLE) 25759138Smsmith return 255; 25859138Smsmith#endif /* _POSIX_VDISABLE */ 25959138Smsmith return c; 26059138Smsmith} 26159138Smsmith 26259138Smsmith/* 26359138Smsmith * Decode a special character from SSH line format. 26459138Smsmith */ 26559138Smsmithstatic cc_t 26659138Smsmithspecial_char_decode(u_int c) 26759138Smsmith{ 26859138Smsmith#ifdef _POSIX_VDISABLE 26959138Smsmith if (c == 255) 27059138Smsmith return _POSIX_VDISABLE; 27159138Smsmith#endif /* _POSIX_VDISABLE */ 27259138Smsmith return c; 27359138Smsmith} 27459138Smsmith 27559138Smsmith/* 27659138Smsmith * Encodes terminal modes for the terminal referenced by fd 27759138Smsmith * or tiop in a portable manner, and appends the modes to a packet 27859138Smsmith * being constructed. 27959138Smsmith */ 28059138Smsmithvoid 28159138Smsmithtty_make_modes(int fd, struct termios *tiop) 28259138Smsmith{ 28359138Smsmith struct termios tio; 28459138Smsmith int baud; 28559138Smsmith Buffer buf; 28659138Smsmith int tty_op_ospeed, tty_op_ispeed; 28759138Smsmith void (*put_arg)(Buffer *, u_int); 28859138Smsmith 28959138Smsmith buffer_init(&buf); 29059138Smsmith if (compat20) { 29159138Smsmith tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 29259138Smsmith tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 29359138Smsmith put_arg = buffer_put_int; 29459138Smsmith } else { 29559138Smsmith tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 29659138Smsmith tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 29759138Smsmith put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 29859138Smsmith } 29959138Smsmith 30059138Smsmith if (tiop == NULL) { 30159138Smsmith if (tcgetattr(fd, &tio) == -1) { 30259138Smsmith logit("tcgetattr: %.100s", strerror(errno)); 30359138Smsmith goto end; 30459138Smsmith } 30559138Smsmith } else 30659138Smsmith tio = *tiop; 30759138Smsmith 30859138Smsmith /* Store input and output baud rates. */ 30959138Smsmith baud = speed_to_baud(cfgetospeed(&tio)); 31059138Smsmith debug3("tty_make_modes: ospeed %d", baud); 31159138Smsmith buffer_put_char(&buf, tty_op_ospeed); 31259138Smsmith buffer_put_int(&buf, baud); 31359138Smsmith baud = speed_to_baud(cfgetispeed(&tio)); 31459138Smsmith debug3("tty_make_modes: ispeed %d", baud); 31559138Smsmith buffer_put_char(&buf, tty_op_ispeed); 31659138Smsmith buffer_put_int(&buf, baud); 31759138Smsmith 31859138Smsmith /* Store values of mode flags. */ 31959138Smsmith#define TTYCHAR(NAME, OP) \ 32059138Smsmith debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 32159138Smsmith buffer_put_char(&buf, OP); \ 32259138Smsmith put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 32359138Smsmith 32459138Smsmith#define TTYMODE(NAME, FIELD, OP) \ 32559138Smsmith debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 32659138Smsmith buffer_put_char(&buf, OP); \ 32759138Smsmith put_arg(&buf, ((tio.FIELD & NAME) != 0)); 32859138Smsmith 32959138Smsmith#include "ttymodes.h" 33059138Smsmith 33159138Smsmith#undef TTYCHAR 33259138Smsmith#undef TTYMODE 33359138Smsmith 33459138Smsmithend: 33559138Smsmith /* Mark end of mode data. */ 33659138Smsmith buffer_put_char(&buf, TTY_OP_END); 33759138Smsmith if (compat20) 33859138Smsmith packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 33959138Smsmith else 34059138Smsmith packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 34159138Smsmith buffer_free(&buf); 34259138Smsmith} 34359138Smsmith 34459138Smsmith/* 34559138Smsmith * Decodes terminal modes for the terminal referenced by fd in a portable 34659138Smsmith * manner from a packet being read. 34759138Smsmith */ 34859138Smsmithvoid 34959138Smsmithtty_parse_modes(int fd, int *n_bytes_ptr) 35059138Smsmith{ 35159138Smsmith struct termios tio; 35259138Smsmith int opcode, baud; 35359138Smsmith int n_bytes = 0; 35459138Smsmith int failure = 0; 35559138Smsmith u_int (*get_arg)(void); 35659138Smsmith int arg, arg_size; 35759138Smsmith 35859138Smsmith if (compat20) { 35959138Smsmith *n_bytes_ptr = packet_get_int(); 36059138Smsmith debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 36159138Smsmith if (*n_bytes_ptr == 0) 36259138Smsmith return; 36359138Smsmith get_arg = packet_get_int; 36459138Smsmith arg_size = 4; 36559138Smsmith } else { 36659138Smsmith get_arg = packet_get_char; 36759138Smsmith arg_size = 1; 36859138Smsmith } 36959138Smsmith 37059138Smsmith /* 37159138Smsmith * Get old attributes for the terminal. We will modify these 37259138Smsmith * flags. I am hoping that if there are any machine-specific 37359138Smsmith * modes, they will initially have reasonable values. 37459138Smsmith */ 37559138Smsmith if (tcgetattr(fd, &tio) == -1) { 37659138Smsmith logit("tcgetattr: %.100s", strerror(errno)); 37759138Smsmith failure = -1; 37859138Smsmith } 37959138Smsmith 38059138Smsmith for (;;) { 38159138Smsmith n_bytes += 1; 38259138Smsmith opcode = packet_get_char(); 38359138Smsmith switch (opcode) { 38459138Smsmith case TTY_OP_END: 38559138Smsmith goto set; 38659138Smsmith 38759138Smsmith /* XXX: future conflict possible */ 38859138Smsmith case TTY_OP_ISPEED_PROTO1: 38959138Smsmith case TTY_OP_ISPEED_PROTO2: 39059138Smsmith n_bytes += 4; 39159138Smsmith baud = packet_get_int(); 39259138Smsmith debug3("tty_parse_modes: ispeed %d", baud); 39359138Smsmith if (failure != -1 && 39459138Smsmith cfsetispeed(&tio, baud_to_speed(baud)) == -1) 39559138Smsmith error("cfsetispeed failed for %d", baud); 39659138Smsmith break; 39759138Smsmith 39859138Smsmith /* XXX: future conflict possible */ 39959138Smsmith case TTY_OP_OSPEED_PROTO1: 40059138Smsmith case TTY_OP_OSPEED_PROTO2: 40159138Smsmith n_bytes += 4; 40259138Smsmith baud = packet_get_int(); 40359138Smsmith debug3("tty_parse_modes: ospeed %d", baud); 40459138Smsmith if (failure != -1 && 40559138Smsmith cfsetospeed(&tio, baud_to_speed(baud)) == -1) 40659138Smsmith error("cfsetospeed failed for %d", baud); 40759138Smsmith break; 40859138Smsmith 40959138Smsmith#define TTYCHAR(NAME, OP) \ 41059138Smsmith case OP: \ 41159138Smsmith n_bytes += arg_size; \ 41259138Smsmith tio.c_cc[NAME] = special_char_decode(get_arg()); \ 41359138Smsmith debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 41459138Smsmith break; 41559138Smsmith#define TTYMODE(NAME, FIELD, OP) \ 41659138Smsmith case OP: \ 41759138Smsmith n_bytes += arg_size; \ 41859138Smsmith if ((arg = get_arg())) \ 41959138Smsmith tio.FIELD |= NAME; \ 42059138Smsmith else \ 42159138Smsmith tio.FIELD &= ~NAME; \ 42259138Smsmith debug3("tty_parse_modes: %d %d", OP, arg); \ 42359138Smsmith break; 42459138Smsmith 42559138Smsmith#include "ttymodes.h" 42659138Smsmith 42759138Smsmith#undef TTYCHAR 42859138Smsmith#undef TTYMODE 42959138Smsmith 43059138Smsmith default: 43159138Smsmith debug("Ignoring unsupported tty mode opcode %d (0x%x)", 43259138Smsmith opcode, opcode); 43359138Smsmith if (!compat20) { 43459138Smsmith /* 43559138Smsmith * SSH1: 43659138Smsmith * Opcodes 1 to 127 are defined to have 43759138Smsmith * a one-byte argument. 43859138Smsmith * Opcodes 128 to 159 are defined to have 43959138Smsmith * an integer argument. 44059138Smsmith */ 44159138Smsmith if (opcode > 0 && opcode < 128) { 44259138Smsmith n_bytes += 1; 44359138Smsmith (void) packet_get_char(); 44459138Smsmith break; 44559138Smsmith } else if (opcode >= 128 && opcode < 160) { 44659138Smsmith n_bytes += 4; 44759138Smsmith (void) packet_get_int(); 44859138Smsmith break; 44959138Smsmith } else { 45059138Smsmith /* 45159138Smsmith * It is a truly undefined opcode (160 to 255). 45259138Smsmith * We have no idea about its arguments. So we 45359138Smsmith * must stop parsing. Note that some data 45459138Smsmith * may be left in the packet; hopefully there 45559138Smsmith * is nothing more coming after the mode data. 45659138Smsmith */ 45759138Smsmith logit("parse_tty_modes: unknown opcode %d", 45859138Smsmith opcode); 45959138Smsmith goto set; 46059138Smsmith } 46159138Smsmith } else { 46259138Smsmith /* 46359138Smsmith * SSH2: 46459138Smsmith * Opcodes 1 to 159 are defined to have 46559138Smsmith * a uint32 argument. 46659138Smsmith * Opcodes 160 to 255 are undefined and 46759138Smsmith * cause parsing to stop. 46859138Smsmith */ 46959138Smsmith if (opcode > 0 && opcode < 160) { 47059138Smsmith n_bytes += 4; 47159138Smsmith (void) packet_get_int(); 47259138Smsmith break; 47359138Smsmith } else { 47459138Smsmith logit("parse_tty_modes: unknown opcode %d", 47559138Smsmith opcode); 47659138Smsmith goto set; 47759138Smsmith } 47859138Smsmith } 47959138Smsmith } 48059138Smsmith } 48159138Smsmith 48259138Smsmithset: 48359138Smsmith if (*n_bytes_ptr != n_bytes) { 48459138Smsmith *n_bytes_ptr = n_bytes; 48559138Smsmith logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 48659138Smsmith *n_bytes_ptr, n_bytes); 48759138Smsmith return; /* Don't process bytes passed */ 48859138Smsmith } 48959138Smsmith if (failure == -1) 49059138Smsmith return; /* Packet parsed ok but tcgetattr() failed */ 49159138Smsmith 49259138Smsmith /* Set the new modes for the terminal. */ 49359138Smsmith if (tcsetattr(fd, TCSANOW, &tio) == -1) 49459138Smsmith logit("Setting tty modes failed: %.100s", strerror(errno)); 49559138Smsmith} 49659138Smsmith