1323124Sdes/* $OpenBSD: ttymodes.c,v 1.30 2016/05/04 14:22:33 markus Exp $ */ 257429Smarkm/* 357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi> 457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 557429Smarkm * All rights reserved 665668Skris * 765668Skris * As far as I am concerned, the code I have written for this software 865668Skris * can be used freely for any purpose. Any derived versions of this 965668Skris * software must be clearly marked as such, and if the derived work is 1065668Skris * incompatible with the protocol description in the RFC file, it must be 1165668Skris * called by a name other than "ssh" or "Secure Shell". 1257429Smarkm */ 1357429Smarkm 1476259Sgreen/* 1576259Sgreen * SSH2 tty modes support by Kevin Steves. 1676259Sgreen * Copyright (c) 2001 Kevin Steves. All rights reserved. 1776259Sgreen * 1876259Sgreen * Redistribution and use in source and binary forms, with or without 1976259Sgreen * modification, are permitted provided that the following conditions 2076259Sgreen * are met: 2176259Sgreen * 1. Redistributions of source code must retain the above copyright 2276259Sgreen * notice, this list of conditions and the following disclaimer. 2376259Sgreen * 2. Redistributions in binary form must reproduce the above copyright 2476259Sgreen * notice, this list of conditions and the following disclaimer in the 2576259Sgreen * documentation and/or other materials provided with the distribution. 2676259Sgreen * 2776259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2876259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2976259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 3076259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 3176259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 3276259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3376259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3476259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3576259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 3676259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3776259Sgreen */ 3876259Sgreen 3976259Sgreen/* 4076259Sgreen * Encoding and decoding of terminal modes in a portable way. 4176259Sgreen * Much of the format is defined in ttymodes.h; it is included multiple times 4276259Sgreen * into this file with the appropriate macro definitions to generate the 4376259Sgreen * suitable code. 4476259Sgreen */ 4576259Sgreen 4657429Smarkm#include "includes.h" 4757429Smarkm 48162852Sdes#include <sys/types.h> 49162852Sdes 50162852Sdes#include <errno.h> 51162852Sdes#include <string.h> 52162852Sdes#include <termios.h> 53162852Sdes#include <stdarg.h> 54162852Sdes 5557429Smarkm#include "packet.h" 5676259Sgreen#include "log.h" 5776259Sgreen#include "compat.h" 5876259Sgreen#include "buffer.h" 5957429Smarkm 6076259Sgreen#define TTY_OP_END 0 6176259Sgreen/* 6276259Sgreen * uint32 (u_int) follows speed in SSH1 and SSH2 6376259Sgreen */ 6476259Sgreen#define TTY_OP_ISPEED_PROTO1 192 6576259Sgreen#define TTY_OP_OSPEED_PROTO1 193 6676259Sgreen#define TTY_OP_ISPEED_PROTO2 128 6776259Sgreen#define TTY_OP_OSPEED_PROTO2 129 6857429Smarkm 6957429Smarkm/* 7057429Smarkm * Converts POSIX speed_t to a baud rate. The values of the 7157429Smarkm * constants for speed_t are not themselves portable. 7257429Smarkm */ 7360573Skrisstatic int 7457429Smarkmspeed_to_baud(speed_t speed) 7557429Smarkm{ 7657429Smarkm switch (speed) { 7757429Smarkm case B0: 7857429Smarkm return 0; 7957429Smarkm case B50: 8057429Smarkm return 50; 8157429Smarkm case B75: 8257429Smarkm return 75; 8357429Smarkm case B110: 8457429Smarkm return 110; 8557429Smarkm case B134: 8657429Smarkm return 134; 8757429Smarkm case B150: 8857429Smarkm return 150; 8957429Smarkm case B200: 9057429Smarkm return 200; 9157429Smarkm case B300: 9257429Smarkm return 300; 9357429Smarkm case B600: 9457429Smarkm return 600; 9557429Smarkm case B1200: 9657429Smarkm return 1200; 9757429Smarkm case B1800: 9857429Smarkm return 1800; 9957429Smarkm case B2400: 10057429Smarkm return 2400; 10157429Smarkm case B4800: 10257429Smarkm return 4800; 10357429Smarkm case B9600: 10457429Smarkm return 9600; 10557429Smarkm 10657429Smarkm#ifdef B19200 10757429Smarkm case B19200: 10857429Smarkm return 19200; 10957429Smarkm#else /* B19200 */ 11057429Smarkm#ifdef EXTA 11157429Smarkm case EXTA: 11257429Smarkm return 19200; 11357429Smarkm#endif /* EXTA */ 11457429Smarkm#endif /* B19200 */ 11557429Smarkm 11657429Smarkm#ifdef B38400 11757429Smarkm case B38400: 11857429Smarkm return 38400; 11957429Smarkm#else /* B38400 */ 12057429Smarkm#ifdef EXTB 12157429Smarkm case EXTB: 12257429Smarkm return 38400; 12357429Smarkm#endif /* EXTB */ 12457429Smarkm#endif /* B38400 */ 12557429Smarkm 12657429Smarkm#ifdef B7200 12757429Smarkm case B7200: 12857429Smarkm return 7200; 12957429Smarkm#endif /* B7200 */ 13057429Smarkm#ifdef B14400 13157429Smarkm case B14400: 13257429Smarkm return 14400; 13357429Smarkm#endif /* B14400 */ 13457429Smarkm#ifdef B28800 13557429Smarkm case B28800: 13657429Smarkm return 28800; 13757429Smarkm#endif /* B28800 */ 13857429Smarkm#ifdef B57600 13957429Smarkm case B57600: 14057429Smarkm return 57600; 14157429Smarkm#endif /* B57600 */ 14257429Smarkm#ifdef B76800 14357429Smarkm case B76800: 14457429Smarkm return 76800; 14557429Smarkm#endif /* B76800 */ 14657429Smarkm#ifdef B115200 14757429Smarkm case B115200: 14857429Smarkm return 115200; 14957429Smarkm#endif /* B115200 */ 15057429Smarkm#ifdef B230400 15157429Smarkm case B230400: 15257429Smarkm return 230400; 15357429Smarkm#endif /* B230400 */ 15457429Smarkm default: 15557429Smarkm return 9600; 15657429Smarkm } 15757429Smarkm} 15857429Smarkm 15957429Smarkm/* 16057429Smarkm * Converts a numeric baud rate to a POSIX speed_t. 16157429Smarkm */ 16260573Skrisstatic speed_t 16357429Smarkmbaud_to_speed(int baud) 16457429Smarkm{ 16557429Smarkm switch (baud) { 16676259Sgreen case 0: 16757429Smarkm return B0; 16857429Smarkm case 50: 16957429Smarkm return B50; 17057429Smarkm case 75: 17157429Smarkm return B75; 17257429Smarkm case 110: 17357429Smarkm return B110; 17457429Smarkm case 134: 17557429Smarkm return B134; 17657429Smarkm case 150: 17757429Smarkm return B150; 17857429Smarkm case 200: 17957429Smarkm return B200; 18057429Smarkm case 300: 18157429Smarkm return B300; 18257429Smarkm case 600: 18357429Smarkm return B600; 18457429Smarkm case 1200: 18557429Smarkm return B1200; 18657429Smarkm case 1800: 18757429Smarkm return B1800; 18857429Smarkm case 2400: 18957429Smarkm return B2400; 19057429Smarkm case 4800: 19157429Smarkm return B4800; 19257429Smarkm case 9600: 19357429Smarkm return B9600; 19457429Smarkm 19557429Smarkm#ifdef B19200 19657429Smarkm case 19200: 19757429Smarkm return B19200; 19857429Smarkm#else /* B19200 */ 19957429Smarkm#ifdef EXTA 20057429Smarkm case 19200: 20157429Smarkm return EXTA; 20257429Smarkm#endif /* EXTA */ 20357429Smarkm#endif /* B19200 */ 20457429Smarkm 20557429Smarkm#ifdef B38400 20657429Smarkm case 38400: 20757429Smarkm return B38400; 20857429Smarkm#else /* B38400 */ 20957429Smarkm#ifdef EXTB 21057429Smarkm case 38400: 21157429Smarkm return EXTB; 21257429Smarkm#endif /* EXTB */ 21357429Smarkm#endif /* B38400 */ 21457429Smarkm 21557429Smarkm#ifdef B7200 21657429Smarkm case 7200: 21757429Smarkm return B7200; 21857429Smarkm#endif /* B7200 */ 21957429Smarkm#ifdef B14400 22057429Smarkm case 14400: 22157429Smarkm return B14400; 22257429Smarkm#endif /* B14400 */ 22357429Smarkm#ifdef B28800 22457429Smarkm case 28800: 22557429Smarkm return B28800; 22657429Smarkm#endif /* B28800 */ 22757429Smarkm#ifdef B57600 22857429Smarkm case 57600: 22957429Smarkm return B57600; 23057429Smarkm#endif /* B57600 */ 23157429Smarkm#ifdef B76800 23257429Smarkm case 76800: 23357429Smarkm return B76800; 23457429Smarkm#endif /* B76800 */ 23557429Smarkm#ifdef B115200 23657429Smarkm case 115200: 23757429Smarkm return B115200; 23857429Smarkm#endif /* B115200 */ 23957429Smarkm#ifdef B230400 24057429Smarkm case 230400: 24157429Smarkm return B230400; 24257429Smarkm#endif /* B230400 */ 24357429Smarkm default: 24457429Smarkm return B9600; 24557429Smarkm } 24657429Smarkm} 24757429Smarkm 24857429Smarkm/* 249149749Sdes * Encode a special character into SSH line format. 250149749Sdes */ 251149749Sdesstatic u_int 252149749Sdesspecial_char_encode(cc_t c) 253149749Sdes{ 254149749Sdes#ifdef _POSIX_VDISABLE 255149749Sdes if (c == _POSIX_VDISABLE) 256149749Sdes return 255; 257149749Sdes#endif /* _POSIX_VDISABLE */ 258149749Sdes return c; 259149749Sdes} 260149749Sdes 261149749Sdes/* 262149749Sdes * Decode a special character from SSH line format. 263149749Sdes */ 264149749Sdesstatic cc_t 265149749Sdesspecial_char_decode(u_int c) 266149749Sdes{ 267149749Sdes#ifdef _POSIX_VDISABLE 268149749Sdes if (c == 255) 269149749Sdes return _POSIX_VDISABLE; 270149749Sdes#endif /* _POSIX_VDISABLE */ 271149749Sdes return c; 272149749Sdes} 273149749Sdes 274149749Sdes/* 27557429Smarkm * Encodes terminal modes for the terminal referenced by fd 27676259Sgreen * or tiop in a portable manner, and appends the modes to a packet 27757429Smarkm * being constructed. 27857429Smarkm */ 27960573Skrisvoid 28076259Sgreentty_make_modes(int fd, struct termios *tiop) 28157429Smarkm{ 28257429Smarkm struct termios tio; 28357429Smarkm int baud; 28476259Sgreen Buffer buf; 28576259Sgreen int tty_op_ospeed, tty_op_ispeed; 28676259Sgreen void (*put_arg)(Buffer *, u_int); 28757429Smarkm 28876259Sgreen buffer_init(&buf); 28976259Sgreen if (compat20) { 29076259Sgreen tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 29176259Sgreen tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 29276259Sgreen put_arg = buffer_put_int; 29376259Sgreen } else { 29476259Sgreen tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 29576259Sgreen tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 29676259Sgreen put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 29757429Smarkm } 29876259Sgreen 29976259Sgreen if (tiop == NULL) { 300181111Sdes if (fd == -1) { 301181111Sdes debug("tty_make_modes: no fd or tio"); 302181111Sdes goto end; 303181111Sdes } 30476259Sgreen if (tcgetattr(fd, &tio) == -1) { 305124208Sdes logit("tcgetattr: %.100s", strerror(errno)); 30676259Sgreen goto end; 30776259Sgreen } 30876259Sgreen } else 30976259Sgreen tio = *tiop; 31076259Sgreen 31157429Smarkm /* Store input and output baud rates. */ 31257429Smarkm baud = speed_to_baud(cfgetospeed(&tio)); 31376259Sgreen buffer_put_char(&buf, tty_op_ospeed); 31476259Sgreen buffer_put_int(&buf, baud); 31557429Smarkm baud = speed_to_baud(cfgetispeed(&tio)); 31676259Sgreen buffer_put_char(&buf, tty_op_ispeed); 31776259Sgreen buffer_put_int(&buf, baud); 31857429Smarkm 31957429Smarkm /* Store values of mode flags. */ 32057429Smarkm#define TTYCHAR(NAME, OP) \ 32176259Sgreen buffer_put_char(&buf, OP); \ 322149749Sdes put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 32376259Sgreen 32457429Smarkm#define TTYMODE(NAME, FIELD, OP) \ 32576259Sgreen buffer_put_char(&buf, OP); \ 32676259Sgreen put_arg(&buf, ((tio.FIELD & NAME) != 0)); 32757429Smarkm 32857429Smarkm#include "ttymodes.h" 32957429Smarkm 33057429Smarkm#undef TTYCHAR 33157429Smarkm#undef TTYMODE 33257429Smarkm 33376259Sgreenend: 33457429Smarkm /* Mark end of mode data. */ 33576259Sgreen buffer_put_char(&buf, TTY_OP_END); 33676259Sgreen if (compat20) 33776259Sgreen packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 33876259Sgreen else 33976259Sgreen packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 34076259Sgreen buffer_free(&buf); 34157429Smarkm} 34257429Smarkm 34357429Smarkm/* 34457429Smarkm * Decodes terminal modes for the terminal referenced by fd in a portable 34557429Smarkm * manner from a packet being read. 34657429Smarkm */ 34760573Skrisvoid 34857429Smarkmtty_parse_modes(int fd, int *n_bytes_ptr) 34957429Smarkm{ 35057429Smarkm struct termios tio; 35157429Smarkm int opcode, baud; 35257429Smarkm int n_bytes = 0; 35357429Smarkm int failure = 0; 35476259Sgreen u_int (*get_arg)(void); 355181111Sdes int arg_size; 35657429Smarkm 35776259Sgreen if (compat20) { 35876259Sgreen *n_bytes_ptr = packet_get_int(); 35976259Sgreen if (*n_bytes_ptr == 0) 36076259Sgreen return; 36176259Sgreen get_arg = packet_get_int; 36276259Sgreen arg_size = 4; 36376259Sgreen } else { 36476259Sgreen get_arg = packet_get_char; 36576259Sgreen arg_size = 1; 36676259Sgreen } 36776259Sgreen 36857429Smarkm /* 36957429Smarkm * Get old attributes for the terminal. We will modify these 37057429Smarkm * flags. I am hoping that if there are any machine-specific 37157429Smarkm * modes, they will initially have reasonable values. 37257429Smarkm */ 37376259Sgreen if (tcgetattr(fd, &tio) == -1) { 374124208Sdes logit("tcgetattr: %.100s", strerror(errno)); 37557429Smarkm failure = -1; 37676259Sgreen } 37757429Smarkm 37857429Smarkm for (;;) { 37957429Smarkm n_bytes += 1; 38057429Smarkm opcode = packet_get_char(); 38157429Smarkm switch (opcode) { 38257429Smarkm case TTY_OP_END: 38357429Smarkm goto set; 38457429Smarkm 38576259Sgreen /* XXX: future conflict possible */ 38676259Sgreen case TTY_OP_ISPEED_PROTO1: 38776259Sgreen case TTY_OP_ISPEED_PROTO2: 38857429Smarkm n_bytes += 4; 38957429Smarkm baud = packet_get_int(); 390162852Sdes if (failure != -1 && 391162852Sdes cfsetispeed(&tio, baud_to_speed(baud)) == -1) 39257429Smarkm error("cfsetispeed failed for %d", baud); 39357429Smarkm break; 39457429Smarkm 39576259Sgreen /* XXX: future conflict possible */ 39676259Sgreen case TTY_OP_OSPEED_PROTO1: 39776259Sgreen case TTY_OP_OSPEED_PROTO2: 39857429Smarkm n_bytes += 4; 39957429Smarkm baud = packet_get_int(); 400162852Sdes if (failure != -1 && 401162852Sdes cfsetospeed(&tio, baud_to_speed(baud)) == -1) 40257429Smarkm error("cfsetospeed failed for %d", baud); 40357429Smarkm break; 40457429Smarkm 40576259Sgreen#define TTYCHAR(NAME, OP) \ 40676259Sgreen case OP: \ 40776259Sgreen n_bytes += arg_size; \ 408149749Sdes tio.c_cc[NAME] = special_char_decode(get_arg()); \ 40957429Smarkm break; 41076259Sgreen#define TTYMODE(NAME, FIELD, OP) \ 41176259Sgreen case OP: \ 41276259Sgreen n_bytes += arg_size; \ 413181111Sdes if (get_arg()) \ 41476259Sgreen tio.FIELD |= NAME; \ 41576259Sgreen else \ 41676259Sgreen tio.FIELD &= ~NAME; \ 41757429Smarkm break; 41857429Smarkm 41957429Smarkm#include "ttymodes.h" 42057429Smarkm 42157429Smarkm#undef TTYCHAR 42257429Smarkm#undef TTYMODE 42357429Smarkm 42457429Smarkm default: 42557429Smarkm debug("Ignoring unsupported tty mode opcode %d (0x%x)", 42692555Sdes opcode, opcode); 42776259Sgreen if (!compat20) { 42876259Sgreen /* 42976259Sgreen * SSH1: 43076259Sgreen * Opcodes 1 to 127 are defined to have 43176259Sgreen * a one-byte argument. 43298675Sdes * Opcodes 128 to 159 are defined to have 43398675Sdes * an integer argument. 43498675Sdes */ 43576259Sgreen if (opcode > 0 && opcode < 128) { 43676259Sgreen n_bytes += 1; 43776259Sgreen (void) packet_get_char(); 43876259Sgreen break; 43976259Sgreen } else if (opcode >= 128 && opcode < 160) { 44098675Sdes n_bytes += 4; 44198675Sdes (void) packet_get_int(); 44298675Sdes break; 44376259Sgreen } else { 44476259Sgreen /* 44576259Sgreen * It is a truly undefined opcode (160 to 255). 44676259Sgreen * We have no idea about its arguments. So we 447162852Sdes * must stop parsing. Note that some data 448162852Sdes * may be left in the packet; hopefully there 449162852Sdes * is nothing more coming after the mode data. 45076259Sgreen */ 451162852Sdes logit("parse_tty_modes: unknown opcode %d", 452162852Sdes opcode); 45376259Sgreen goto set; 45498675Sdes } 45557429Smarkm } else { 45657429Smarkm /* 45776259Sgreen * SSH2: 45876259Sgreen * Opcodes 1 to 159 are defined to have 45976259Sgreen * a uint32 argument. 46076259Sgreen * Opcodes 160 to 255 are undefined and 46176259Sgreen * cause parsing to stop. 46257429Smarkm */ 46376259Sgreen if (opcode > 0 && opcode < 160) { 46457429Smarkm n_bytes += 4; 46557429Smarkm (void) packet_get_int(); 46657429Smarkm break; 46776259Sgreen } else { 468162852Sdes logit("parse_tty_modes: unknown opcode %d", 469162852Sdes opcode); 47076259Sgreen goto set; 47157429Smarkm } 47298675Sdes } 47357429Smarkm } 47457429Smarkm } 47557429Smarkm 47657429Smarkmset: 47757429Smarkm if (*n_bytes_ptr != n_bytes) { 47857429Smarkm *n_bytes_ptr = n_bytes; 479124208Sdes logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 48076259Sgreen *n_bytes_ptr, n_bytes); 48157429Smarkm return; /* Don't process bytes passed */ 48257429Smarkm } 48357429Smarkm if (failure == -1) 48476259Sgreen return; /* Packet parsed ok but tcgetattr() failed */ 48557429Smarkm 48657429Smarkm /* Set the new modes for the terminal. */ 48776259Sgreen if (tcsetattr(fd, TCSANOW, &tio) == -1) 488124208Sdes logit("Setting tty modes failed: %.100s", strerror(errno)); 48957429Smarkm} 490