ttymodes.c revision 162853
11541Srgrimes/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */ 21541Srgrimes/* 31541Srgrimes * Author: Tatu Ylonen <ylo@cs.hut.fi> 41541Srgrimes * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 51541Srgrimes * All rights reserved 61541Srgrimes * 71541Srgrimes * As far as I am concerned, the code I have written for this software 81541Srgrimes * can be used freely for any purpose. Any derived versions of this 91541Srgrimes * software must be clearly marked as such, and if the derived work is 101541Srgrimes * incompatible with the protocol description in the RFC file, it must be 111541Srgrimes * called by a name other than "ssh" or "Secure Shell". 121541Srgrimes */ 131541Srgrimes 141541Srgrimes/* 151541Srgrimes * SSH2 tty modes support by Kevin Steves. 161541Srgrimes * Copyright (c) 2001 Kevin Steves. All rights reserved. 171541Srgrimes * 181541Srgrimes * Redistribution and use in source and binary forms, with or without 191541Srgrimes * modification, are permitted provided that the following conditions 201541Srgrimes * are met: 211541Srgrimes * 1. Redistributions of source code must retain the above copyright 221541Srgrimes * notice, this list of conditions and the following disclaimer. 231541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 241541Srgrimes * notice, this list of conditions and the following disclaimer in the 251541Srgrimes * documentation and/or other materials provided with the distribution. 261541Srgrimes * 271541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 281541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 291541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 301541Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 311541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 321541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 331541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 341541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 351541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 361817Sdg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 371541Srgrimes */ 381541Srgrimes 391541Srgrimes/* 401541Srgrimes * Encoding and decoding of terminal modes in a portable way. 411541Srgrimes * Much of the format is defined in ttymodes.h; it is included multiple times 421541Srgrimes * into this file with the appropriate macro definitions to generate the 431541Srgrimes * suitable code. 441541Srgrimes */ 451541Srgrimes 461541Srgrimes#include "includes.h" 471541Srgrimes 481541Srgrimes#include <sys/types.h> 491541Srgrimes 501541Srgrimes#include <errno.h> 511541Srgrimes#include <string.h> 521541Srgrimes#include <termios.h> 531541Srgrimes#include <stdarg.h> 541541Srgrimes 551541Srgrimes#include "packet.h" 561541Srgrimes#include "log.h" 571541Srgrimes#include "ssh1.h" 581541Srgrimes#include "compat.h" 591541Srgrimes#include "buffer.h" 601541Srgrimes 611541Srgrimes#define TTY_OP_END 0 621541Srgrimes/* 631817Sdg * uint32 (u_int) follows speed in SSH1 and SSH2 641817Sdg */ 651541Srgrimes#define TTY_OP_ISPEED_PROTO1 192 661541Srgrimes#define TTY_OP_OSPEED_PROTO1 193 671541Srgrimes#define TTY_OP_ISPEED_PROTO2 128 681541Srgrimes#define TTY_OP_OSPEED_PROTO2 129 691541Srgrimes 701541Srgrimes/* 711541Srgrimes * Converts POSIX speed_t to a baud rate. The values of the 721541Srgrimes * constants for speed_t are not themselves portable. 731541Srgrimes */ 741541Srgrimesstatic int 751541Srgrimesspeed_to_baud(speed_t speed) 761541Srgrimes{ 771541Srgrimes switch (speed) { 781541Srgrimes case B0: 791541Srgrimes return 0; 801541Srgrimes case B50: 811541Srgrimes return 50; 821541Srgrimes case B75: 831541Srgrimes return 75; 841541Srgrimes case B110: 851541Srgrimes return 110; 861541Srgrimes case B134: 871541Srgrimes return 134; 881541Srgrimes case B150: 891541Srgrimes return 150; 901541Srgrimes case B200: 911541Srgrimes return 200; 921541Srgrimes case B300: 931541Srgrimes return 300; 941541Srgrimes case B600: 951541Srgrimes return 600; 961541Srgrimes case B1200: 971541Srgrimes return 1200; 981541Srgrimes case B1800: 991541Srgrimes return 1800; 1001541Srgrimes case B2400: 1011541Srgrimes return 2400; 1021541Srgrimes case B4800: 1031541Srgrimes return 4800; 1041541Srgrimes case B9600: 1051541Srgrimes return 9600; 1061541Srgrimes 1071541Srgrimes#ifdef B19200 1081541Srgrimes case B19200: 1091541Srgrimes return 19200; 1101541Srgrimes#else /* B19200 */ 1111541Srgrimes#ifdef EXTA 1121541Srgrimes case EXTA: 1131541Srgrimes return 19200; 1141541Srgrimes#endif /* EXTA */ 1151541Srgrimes#endif /* B19200 */ 1161541Srgrimes 1171541Srgrimes#ifdef B38400 1181541Srgrimes case B38400: 1191541Srgrimes return 38400; 1201541Srgrimes#else /* B38400 */ 1211541Srgrimes#ifdef EXTB 1221541Srgrimes case EXTB: 1231541Srgrimes return 38400; 1241541Srgrimes#endif /* EXTB */ 1251541Srgrimes#endif /* B38400 */ 1261541Srgrimes 1271541Srgrimes#ifdef B7200 1281541Srgrimes case B7200: 1291541Srgrimes return 7200; 1301541Srgrimes#endif /* B7200 */ 1311541Srgrimes#ifdef B14400 1321541Srgrimes case B14400: 1331541Srgrimes return 14400; 1341541Srgrimes#endif /* B14400 */ 1351541Srgrimes#ifdef B28800 1361541Srgrimes case B28800: 1371541Srgrimes return 28800; 1381541Srgrimes#endif /* B28800 */ 1391541Srgrimes#ifdef B57600 1401541Srgrimes case B57600: 1411541Srgrimes return 57600; 1421541Srgrimes#endif /* B57600 */ 1431541Srgrimes#ifdef B76800 1441541Srgrimes case B76800: 1451541Srgrimes return 76800; 1461541Srgrimes#endif /* B76800 */ 1471541Srgrimes#ifdef B115200 1481541Srgrimes case B115200: 1491541Srgrimes return 115200; 1501541Srgrimes#endif /* B115200 */ 1511541Srgrimes#ifdef B230400 1521541Srgrimes case B230400: 1531541Srgrimes return 230400; 1541541Srgrimes#endif /* B230400 */ 1551541Srgrimes default: 1561541Srgrimes return 9600; 1571541Srgrimes } 1581541Srgrimes} 1591541Srgrimes 1601541Srgrimes/* 1611541Srgrimes * Converts a numeric baud rate to a POSIX speed_t. 1621541Srgrimes */ 1631541Srgrimesstatic speed_t 1641541Srgrimesbaud_to_speed(int baud) 1651541Srgrimes{ 1661541Srgrimes switch (baud) { 1671541Srgrimes case 0: 1681541Srgrimes return B0; 1691541Srgrimes case 50: 1701541Srgrimes return B50; 1711541Srgrimes case 75: 1721541Srgrimes return B75; 1731541Srgrimes case 110: 1741541Srgrimes return B110; 1751541Srgrimes case 134: 1761541Srgrimes return B134; 1771541Srgrimes case 150: 1781541Srgrimes return B150; 1791541Srgrimes case 200: 1801541Srgrimes return B200; 1811541Srgrimes case 300: 1821541Srgrimes return B300; 1831541Srgrimes case 600: 1841541Srgrimes return B600; 1851541Srgrimes case 1200: 1861541Srgrimes return B1200; 1871541Srgrimes case 1800: 1881541Srgrimes return B1800; 1891541Srgrimes case 2400: 1901541Srgrimes return B2400; 1911541Srgrimes case 4800: 1921541Srgrimes return B4800; 1931541Srgrimes case 9600: 1941541Srgrimes return B9600; 1951541Srgrimes 1961541Srgrimes#ifdef B19200 1971541Srgrimes case 19200: 1981541Srgrimes return B19200; 1991541Srgrimes#else /* B19200 */ 2001541Srgrimes#ifdef EXTA 2011541Srgrimes case 19200: 2021541Srgrimes return EXTA; 2031541Srgrimes#endif /* EXTA */ 2041541Srgrimes#endif /* B19200 */ 2051541Srgrimes 2061541Srgrimes#ifdef B38400 2071541Srgrimes case 38400: 2081541Srgrimes return B38400; 2091541Srgrimes#else /* B38400 */ 2101541Srgrimes#ifdef EXTB 2111541Srgrimes case 38400: 2121541Srgrimes return EXTB; 2131541Srgrimes#endif /* EXTB */ 2141541Srgrimes#endif /* B38400 */ 2151541Srgrimes 2161541Srgrimes#ifdef B7200 2171541Srgrimes case 7200: 2181541Srgrimes return B7200; 2191541Srgrimes#endif /* B7200 */ 2201541Srgrimes#ifdef B14400 2211541Srgrimes case 14400: 2221541Srgrimes return B14400; 2231541Srgrimes#endif /* B14400 */ 2241541Srgrimes#ifdef B28800 2251541Srgrimes case 28800: 2261541Srgrimes return B28800; 2271541Srgrimes#endif /* B28800 */ 2281541Srgrimes#ifdef B57600 2291541Srgrimes case 57600: 2301541Srgrimes return B57600; 2311541Srgrimes#endif /* B57600 */ 2321541Srgrimes#ifdef B76800 2331541Srgrimes case 76800: 2341541Srgrimes return B76800; 2351541Srgrimes#endif /* B76800 */ 2361541Srgrimes#ifdef B115200 2371541Srgrimes case 115200: 2381541Srgrimes return B115200; 2391541Srgrimes#endif /* B115200 */ 2401541Srgrimes#ifdef B230400 2411541Srgrimes case 230400: 2421541Srgrimes return B230400; 2431541Srgrimes#endif /* B230400 */ 2441541Srgrimes default: 2451541Srgrimes return B9600; 2461541Srgrimes } 2471541Srgrimes} 2481541Srgrimes 2491541Srgrimes/* 2501541Srgrimes * Encode a special character into SSH line format. 2511541Srgrimes */ 2521541Srgrimesstatic u_int 2531541Srgrimesspecial_char_encode(cc_t c) 2541541Srgrimes{ 2551541Srgrimes#ifdef _POSIX_VDISABLE 2561541Srgrimes if (c == _POSIX_VDISABLE) 2571541Srgrimes return 255; 2581541Srgrimes#endif /* _POSIX_VDISABLE */ 2591541Srgrimes return c; 2601541Srgrimes} 2611541Srgrimes 2621541Srgrimes/* 2631541Srgrimes * Decode a special character from SSH line format. 2641541Srgrimes */ 2651541Srgrimesstatic cc_t 2661541Srgrimesspecial_char_decode(u_int c) 2671541Srgrimes{ 2681541Srgrimes#ifdef _POSIX_VDISABLE 2691541Srgrimes if (c == 255) 2701541Srgrimes return _POSIX_VDISABLE; 2711541Srgrimes#endif /* _POSIX_VDISABLE */ 2721541Srgrimes return c; 2731541Srgrimes} 2741541Srgrimes 2751541Srgrimes/* 2761541Srgrimes * Encodes terminal modes for the terminal referenced by fd 2771541Srgrimes * or tiop in a portable manner, and appends the modes to a packet 2781541Srgrimes * being constructed. 2791541Srgrimes */ 2801541Srgrimesvoid 2811541Srgrimestty_make_modes(int fd, struct termios *tiop) 2821541Srgrimes{ 2831541Srgrimes struct termios tio; 2841541Srgrimes int baud; 2851541Srgrimes Buffer buf; 2861541Srgrimes int tty_op_ospeed, tty_op_ispeed; 2871541Srgrimes void (*put_arg)(Buffer *, u_int); 2881541Srgrimes 2891541Srgrimes buffer_init(&buf); 2901541Srgrimes if (compat20) { 2911541Srgrimes tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 2921541Srgrimes tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 2931541Srgrimes put_arg = buffer_put_int; 2941541Srgrimes } else { 2951541Srgrimes tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 2961541Srgrimes tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 2971549Srgrimes put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 2981541Srgrimes } 2991541Srgrimes 3001541Srgrimes if (tiop == NULL) { 3011549Srgrimes if (tcgetattr(fd, &tio) == -1) { 3021549Srgrimes logit("tcgetattr: %.100s", strerror(errno)); 3031549Srgrimes goto end; 3041541Srgrimes } 3051541Srgrimes } else 3061541Srgrimes tio = *tiop; 3071541Srgrimes 3081541Srgrimes /* Store input and output baud rates. */ 3091541Srgrimes baud = speed_to_baud(cfgetospeed(&tio)); 3101541Srgrimes debug3("tty_make_modes: ospeed %d", baud); 3111541Srgrimes buffer_put_char(&buf, tty_op_ospeed); 3121541Srgrimes buffer_put_int(&buf, baud); 3131541Srgrimes baud = speed_to_baud(cfgetispeed(&tio)); 3141541Srgrimes debug3("tty_make_modes: ispeed %d", baud); 3151541Srgrimes buffer_put_char(&buf, tty_op_ispeed); 3161541Srgrimes buffer_put_int(&buf, baud); 3171541Srgrimes 3181541Srgrimes /* Store values of mode flags. */ 3191541Srgrimes#define TTYCHAR(NAME, OP) \ 3201541Srgrimes debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 3211541Srgrimes buffer_put_char(&buf, OP); \ 3221541Srgrimes put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 3231541Srgrimes 3241541Srgrimes#define TTYMODE(NAME, FIELD, OP) \ 3251541Srgrimes debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 3261541Srgrimes buffer_put_char(&buf, OP); \ 3271541Srgrimes put_arg(&buf, ((tio.FIELD & NAME) != 0)); 3281541Srgrimes 3291541Srgrimes#include "ttymodes.h" 3301541Srgrimes 3311541Srgrimes#undef TTYCHAR 3321541Srgrimes#undef TTYMODE 3331541Srgrimes 3341541Srgrimesend: 3351541Srgrimes /* Mark end of mode data. */ 3361541Srgrimes buffer_put_char(&buf, TTY_OP_END); 3371541Srgrimes if (compat20) 3381541Srgrimes packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 3391541Srgrimes else 3401541Srgrimes packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 3411541Srgrimes buffer_free(&buf); 3421541Srgrimes} 3431541Srgrimes 3441541Srgrimes/* 3451541Srgrimes * Decodes terminal modes for the terminal referenced by fd in a portable 3461541Srgrimes * manner from a packet being read. 3471541Srgrimes */ 3481541Srgrimesvoid 3491541Srgrimestty_parse_modes(int fd, int *n_bytes_ptr) 3501541Srgrimes{ 3511541Srgrimes struct termios tio; 3521541Srgrimes int opcode, baud; 3531541Srgrimes int n_bytes = 0; 3541541Srgrimes int failure = 0; 3551541Srgrimes u_int (*get_arg)(void); 3561541Srgrimes int arg, arg_size; 3571541Srgrimes 3581541Srgrimes if (compat20) { 3591541Srgrimes *n_bytes_ptr = packet_get_int(); 3601541Srgrimes debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 3611541Srgrimes if (*n_bytes_ptr == 0) 3621541Srgrimes return; 3631541Srgrimes get_arg = packet_get_int; 3641541Srgrimes arg_size = 4; 3651541Srgrimes } else { 3661541Srgrimes get_arg = packet_get_char; 3671541Srgrimes arg_size = 1; 3681541Srgrimes } 3691541Srgrimes 3701541Srgrimes /* 3711541Srgrimes * Get old attributes for the terminal. We will modify these 3721541Srgrimes * flags. I am hoping that if there are any machine-specific 3731541Srgrimes * modes, they will initially have reasonable values. 3741541Srgrimes */ 3751541Srgrimes if (tcgetattr(fd, &tio) == -1) { 3761541Srgrimes logit("tcgetattr: %.100s", strerror(errno)); 3771541Srgrimes failure = -1; 3781541Srgrimes } 3791541Srgrimes 3801541Srgrimes for (;;) { 3811541Srgrimes n_bytes += 1; 3821541Srgrimes opcode = packet_get_char(); 3831541Srgrimes switch (opcode) { 3841541Srgrimes case TTY_OP_END: 3851541Srgrimes goto set; 3861541Srgrimes 3871541Srgrimes /* XXX: future conflict possible */ 3881541Srgrimes case TTY_OP_ISPEED_PROTO1: 3891541Srgrimes case TTY_OP_ISPEED_PROTO2: 3901541Srgrimes n_bytes += 4; 3911541Srgrimes baud = packet_get_int(); 3921541Srgrimes debug3("tty_parse_modes: ispeed %d", baud); 3931541Srgrimes if (failure != -1 && 3941541Srgrimes cfsetispeed(&tio, baud_to_speed(baud)) == -1) 3951541Srgrimes error("cfsetispeed failed for %d", baud); 3961541Srgrimes break; 3971541Srgrimes 3981541Srgrimes /* XXX: future conflict possible */ 3991541Srgrimes case TTY_OP_OSPEED_PROTO1: 4001541Srgrimes case TTY_OP_OSPEED_PROTO2: 4011541Srgrimes n_bytes += 4; 4021541Srgrimes baud = packet_get_int(); 4031541Srgrimes debug3("tty_parse_modes: ospeed %d", baud); 4041541Srgrimes if (failure != -1 && 4051541Srgrimes cfsetospeed(&tio, baud_to_speed(baud)) == -1) 4061541Srgrimes error("cfsetospeed failed for %d", baud); 4071541Srgrimes break; 4081541Srgrimes 4091541Srgrimes#define TTYCHAR(NAME, OP) \ 4101541Srgrimes case OP: \ 4111541Srgrimes n_bytes += arg_size; \ 4121541Srgrimes tio.c_cc[NAME] = special_char_decode(get_arg()); \ 4131549Srgrimes debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 4141541Srgrimes break; 4151541Srgrimes#define TTYMODE(NAME, FIELD, OP) \ 4161541Srgrimes case OP: \ 4171541Srgrimes n_bytes += arg_size; \ 4181541Srgrimes if ((arg = get_arg())) \ 4191541Srgrimes tio.FIELD |= NAME; \ 4201541Srgrimes else \ 4211541Srgrimes tio.FIELD &= ~NAME; \ 4221541Srgrimes debug3("tty_parse_modes: %d %d", OP, arg); \ 4231541Srgrimes break; 4241541Srgrimes 4251541Srgrimes#include "ttymodes.h" 4261541Srgrimes 4271541Srgrimes#undef TTYCHAR 4281541Srgrimes#undef TTYMODE 4291541Srgrimes 4301541Srgrimes default: 4311541Srgrimes debug("Ignoring unsupported tty mode opcode %d (0x%x)", 4321541Srgrimes opcode, opcode); 4331541Srgrimes if (!compat20) { 4341541Srgrimes /* 4351541Srgrimes * SSH1: 4361541Srgrimes * Opcodes 1 to 127 are defined to have 4371541Srgrimes * a one-byte argument. 4381541Srgrimes * Opcodes 128 to 159 are defined to have 4391541Srgrimes * an integer argument. 4401541Srgrimes */ 4411541Srgrimes if (opcode > 0 && opcode < 128) { 4421541Srgrimes n_bytes += 1; 4431541Srgrimes (void) packet_get_char(); 4441541Srgrimes break; 4451541Srgrimes } else if (opcode >= 128 && opcode < 160) { 4461541Srgrimes n_bytes += 4; 4471541Srgrimes (void) packet_get_int(); 4481541Srgrimes break; 4491541Srgrimes } else { 4501541Srgrimes /* 4511541Srgrimes * It is a truly undefined opcode (160 to 255). 4521541Srgrimes * We have no idea about its arguments. So we 4531541Srgrimes * must stop parsing. Note that some data 4541541Srgrimes * may be left in the packet; hopefully there 4551541Srgrimes * is nothing more coming after the mode data. 4561541Srgrimes */ 457 logit("parse_tty_modes: unknown opcode %d", 458 opcode); 459 goto set; 460 } 461 } else { 462 /* 463 * SSH2: 464 * Opcodes 1 to 159 are defined to have 465 * a uint32 argument. 466 * Opcodes 160 to 255 are undefined and 467 * cause parsing to stop. 468 */ 469 if (opcode > 0 && opcode < 160) { 470 n_bytes += 4; 471 (void) packet_get_int(); 472 break; 473 } else { 474 logit("parse_tty_modes: unknown opcode %d", 475 opcode); 476 goto set; 477 } 478 } 479 } 480 } 481 482set: 483 if (*n_bytes_ptr != n_bytes) { 484 *n_bytes_ptr = n_bytes; 485 logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 486 *n_bytes_ptr, n_bytes); 487 return; /* Don't process bytes passed */ 488 } 489 if (failure == -1) 490 return; /* Packet parsed ok but tcgetattr() failed */ 491 492 /* Set the new modes for the terminal. */ 493 if (tcsetattr(fd, TCSANOW, &tio) == -1) 494 logit("Setting tty modes failed: %.100s", strerror(errno)); 495} 496