1 /*
   2  * Author: Tatu Ylonen <ylo@cs.hut.fi>
   3  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
   4  *                    All rights reserved
   5  *
   6  * As far as I am concerned, the code I have written for this software
   7  * can be used freely for any purpose.  Any derived versions of this
   8  * software must be clearly marked as such, and if the derived work is
   9  * incompatible with the protocol description in the RFC file, it must be
  10  * called by a name other than "ssh" or "Secure Shell".
  11  */
  12 
  13 /*
  14  * SSH2 tty modes support by Kevin Steves.
  15  * Copyright (c) 2001 Kevin Steves.  All rights reserved.
  16  *
  17  * Redistribution and use in source and binary forms, with or without
  18  * modification, are permitted provided that the following conditions
  19  * are met:
  20  * 1. Redistributions of source code must retain the above copyright
  21  *    notice, this list of conditions and the following disclaimer.
  22  * 2. Redistributions in binary form must reproduce the above copyright
  23  *    notice, this list of conditions and the following disclaimer in the
  24  *    documentation and/or other materials provided with the distribution.
  25  *
  26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  36  */
  37 
  38 /*
  39  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  40  * Use is subject to license terms.
  41  */
  42 
  43 /*
  44  * Encoding and decoding of terminal modes in a portable way.
  45  * Much of the format is defined in ttymodes.h; it is included multiple times
  46  * into this file with the appropriate macro definitions to generate the
  47  * suitable code.
  48  */
  49 
  50 #include "includes.h"
  51 RCSID("$OpenBSD: ttymodes.c,v 1.18 2002/06/19 00:27:55 deraadt Exp $");
  52 
  53 #include "packet.h"
  54 #include "log.h"
  55 #include "ssh1.h"
  56 #include "compat.h"
  57 #include "buffer.h"
  58 #include "bufaux.h"
  59 
  60 #define TTY_OP_END              0
  61 /*
  62  * uint32 (u_int) follows speed in SSH1 and SSH2
  63  */
  64 #define TTY_OP_ISPEED_PROTO1    192
  65 #define TTY_OP_OSPEED_PROTO1    193
  66 #define TTY_OP_ISPEED_PROTO2    128
  67 #define TTY_OP_OSPEED_PROTO2    129
  68 
  69 /*
  70  * Converts POSIX speed_t to a baud rate.  The values of the
  71  * constants for speed_t are not themselves portable.
  72  */
  73 static int
  74 speed_to_baud(speed_t speed)
  75 {
  76         switch (speed) {
  77         case B0:
  78                 return 0;
  79         case B50:
  80                 return 50;
  81         case B75:
  82                 return 75;
  83         case B110:
  84                 return 110;
  85         case B134:
  86                 return 134;
  87         case B150:
  88                 return 150;
  89         case B200:
  90                 return 200;
  91         case B300:
  92                 return 300;
  93         case B600:
  94                 return 600;
  95         case B1200:
  96                 return 1200;
  97         case B1800:
  98                 return 1800;
  99         case B2400:
 100                 return 2400;
 101         case B4800:
 102                 return 4800;
 103         case B9600:
 104                 return 9600;
 105 
 106 #ifdef B19200
 107         case B19200:
 108                 return 19200;
 109 #else /* B19200 */
 110 #ifdef EXTA
 111         case EXTA:
 112                 return 19200;
 113 #endif /* EXTA */
 114 #endif /* B19200 */
 115 
 116 #ifdef B38400
 117         case B38400:
 118                 return 38400;
 119 #else /* B38400 */
 120 #ifdef EXTB
 121         case EXTB:
 122                 return 38400;
 123 #endif /* EXTB */
 124 #endif /* B38400 */
 125 
 126 #ifdef B7200
 127         case B7200:
 128                 return 7200;
 129 #endif /* B7200 */
 130 #ifdef B14400
 131         case B14400:
 132                 return 14400;
 133 #endif /* B14400 */
 134 #ifdef B28800
 135         case B28800:
 136                 return 28800;
 137 #endif /* B28800 */
 138 #ifdef B57600
 139         case B57600:
 140                 return 57600;
 141 #endif /* B57600 */
 142 #ifdef B76800
 143         case B76800:
 144                 return 76800;
 145 #endif /* B76800 */
 146 #ifdef B115200
 147         case B115200:
 148                 return 115200;
 149 #endif /* B115200 */
 150 #ifdef B230400
 151         case B230400:
 152                 return 230400;
 153 #endif /* B230400 */
 154 #ifdef B460800
 155         case B460800:
 156                 return 460800;
 157 #endif /* B460800 */
 158 #ifdef B921600
 159         case B921600:
 160                 return 921600;
 161 #endif /* B921600 */
 162         default:
 163                 return 9600;
 164         }
 165 }
 166 
 167 /*
 168  * Converts a numeric baud rate to a POSIX speed_t.
 169  */
 170 static speed_t
 171 baud_to_speed(int baud)
 172 {
 173         switch (baud) {
 174         case 0:
 175                 return B0;
 176         case 50:
 177                 return B50;
 178         case 75:
 179                 return B75;
 180         case 110:
 181                 return B110;
 182         case 134:
 183                 return B134;
 184         case 150:
 185                 return B150;
 186         case 200:
 187                 return B200;
 188         case 300:
 189                 return B300;
 190         case 600:
 191                 return B600;
 192         case 1200:
 193                 return B1200;
 194         case 1800:
 195                 return B1800;
 196         case 2400:
 197                 return B2400;
 198         case 4800:
 199                 return B4800;
 200         case 9600:
 201                 return B9600;
 202 
 203 #ifdef B19200
 204         case 19200:
 205                 return B19200;
 206 #else /* B19200 */
 207 #ifdef EXTA
 208         case 19200:
 209                 return EXTA;
 210 #endif /* EXTA */
 211 #endif /* B19200 */
 212 
 213 #ifdef B38400
 214         case 38400:
 215                 return B38400;
 216 #else /* B38400 */
 217 #ifdef EXTB
 218         case 38400:
 219                 return EXTB;
 220 #endif /* EXTB */
 221 #endif /* B38400 */
 222 
 223 #ifdef B7200
 224         case 7200:
 225                 return B7200;
 226 #endif /* B7200 */
 227 #ifdef B14400
 228         case 14400:
 229                 return B14400;
 230 #endif /* B14400 */
 231 #ifdef B28800
 232         case 28800:
 233                 return B28800;
 234 #endif /* B28800 */
 235 #ifdef B57600
 236         case 57600:
 237                 return B57600;
 238 #endif /* B57600 */
 239 #ifdef B76800
 240         case 76800:
 241                 return B76800;
 242 #endif /* B76800 */
 243 #ifdef B115200
 244         case 115200:
 245                 return B115200;
 246 #endif /* B115200 */
 247 #ifdef B230400
 248         case 230400:
 249                 return B230400;
 250 #endif /* B230400 */
 251 #ifdef B460800
 252         case 460800:
 253                 return B460800;
 254 #endif /* B460800 */
 255 #ifdef B921600
 256         case 921600:
 257                 return B921600;
 258 #endif /* B921600 */
 259         default:
 260                 return B9600;
 261         }
 262 }
 263 
 264 /*
 265  * Encodes terminal modes for the terminal referenced by fd
 266  * or tiop in a portable manner, and appends the modes to a packet
 267  * being constructed.
 268  */
 269 void
 270 tty_make_modes(int fd, struct termios *tiop)
 271 {
 272         struct termios tio;
 273         int baud;
 274         Buffer buf;
 275         int tty_op_ospeed, tty_op_ispeed;
 276         void (*put_arg)(Buffer *, u_int);
 277 
 278         buffer_init(&buf);
 279         if (compat20) {
 280                 tty_op_ospeed = TTY_OP_OSPEED_PROTO2;
 281                 tty_op_ispeed = TTY_OP_ISPEED_PROTO2;
 282                 put_arg = buffer_put_int;
 283         } else {
 284                 tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
 285                 tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
 286                 put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
 287         }
 288 
 289         if (tiop == NULL) {
 290                 if (tcgetattr(fd, &tio) == -1) {
 291                         log("tcgetattr: %.100s", strerror(errno));
 292                         goto end;
 293                 }
 294         } else
 295                 tio = *tiop;
 296 
 297         /* Store input and output baud rates. */
 298         baud = speed_to_baud(cfgetospeed(&tio));
 299         debug3("tty_make_modes: ospeed %d", baud);
 300         buffer_put_char(&buf, tty_op_ospeed);
 301         buffer_put_int(&buf, baud);
 302         baud = speed_to_baud(cfgetispeed(&tio));
 303         debug3("tty_make_modes: ispeed %d", baud);
 304         buffer_put_char(&buf, tty_op_ispeed);
 305         buffer_put_int(&buf, baud);
 306 
 307         /* Store values of mode flags. */
 308 #define TTYCHAR(NAME, OP) \
 309         debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \
 310         buffer_put_char(&buf, OP); \
 311         put_arg(&buf, tio.c_cc[NAME]);
 312 
 313 #define TTYMODE(NAME, FIELD, OP) \
 314         debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \
 315         buffer_put_char(&buf, OP); \
 316         put_arg(&buf, ((tio.FIELD & NAME) != 0));
 317 
 318 #include "ttymodes.h"
 319 
 320 #undef TTYCHAR
 321 #undef TTYMODE
 322 
 323 end:
 324         /* Mark end of mode data. */
 325         buffer_put_char(&buf, TTY_OP_END);
 326         if (compat20)
 327                 packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
 328         else
 329                 packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
 330         buffer_free(&buf);
 331 }
 332 
 333 /*
 334  * Decodes terminal modes for the terminal referenced by fd in a portable
 335  * manner from a packet being read.
 336  */
 337 void
 338 tty_parse_modes(int fd, int *n_bytes_ptr)
 339 {
 340         struct termios tio;
 341         int opcode, baud;
 342         int n_bytes = 0;
 343         int failure = 0;
 344         u_int (*get_arg)(void);
 345         int arg, arg_size;
 346 
 347         if (compat20) {
 348                 *n_bytes_ptr = packet_get_int();
 349                 debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr);
 350                 if (*n_bytes_ptr == 0)
 351                         return;
 352                 get_arg = packet_get_int;
 353                 arg_size = 4;
 354         } else {
 355                 get_arg = packet_get_char;
 356                 arg_size = 1;
 357         }
 358 
 359         /*
 360          * Get old attributes for the terminal.  We will modify these
 361          * flags. I am hoping that if there are any machine-specific
 362          * modes, they will initially have reasonable values.
 363          */
 364         if (tcgetattr(fd, &tio) == -1) {
 365                 log("tcgetattr: %.100s", strerror(errno));
 366                 failure = -1;
 367         }
 368 
 369         for (;;) {
 370                 n_bytes += 1;
 371                 opcode = packet_get_char();
 372                 switch (opcode) {
 373                 case TTY_OP_END:
 374                         goto set;
 375 
 376                 /* XXX: future conflict possible */
 377                 case TTY_OP_ISPEED_PROTO1:
 378                 case TTY_OP_ISPEED_PROTO2:
 379                         n_bytes += 4;
 380                         baud = packet_get_int();
 381                         debug3("tty_parse_modes: ispeed %d", baud);
 382                         if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1)
 383                                 error("cfsetispeed failed for %d", baud);
 384                         break;
 385 
 386                 /* XXX: future conflict possible */
 387                 case TTY_OP_OSPEED_PROTO1:
 388                 case TTY_OP_OSPEED_PROTO2:
 389                         n_bytes += 4;
 390                         baud = packet_get_int();
 391                         debug3("tty_parse_modes: ospeed %d", baud);
 392                         if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1)
 393                                 error("cfsetospeed failed for %d", baud);
 394                         break;
 395 
 396 #define TTYCHAR(NAME, OP) \
 397         case OP: \
 398           n_bytes += arg_size; \
 399           tio.c_cc[NAME] = get_arg(); \
 400           debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \
 401           break;
 402 #define TTYMODE(NAME, FIELD, OP) \
 403         case OP: \
 404           n_bytes += arg_size; \
 405           if ((arg = get_arg())) \
 406             tio.FIELD |= NAME; \
 407           else \
 408             tio.FIELD &= ~NAME;     \
 409           debug3("tty_parse_modes: %d %d", OP, arg); \
 410           break;
 411 
 412 #include "ttymodes.h"
 413 
 414 #undef TTYCHAR
 415 #undef TTYMODE
 416 
 417                 default:
 418                         debug("Ignoring unsupported tty mode opcode %d (0x%x)",
 419                             opcode, opcode);
 420                         if (!compat20) {
 421                                 /*
 422                                  * SSH1:
 423                                  * Opcodes 1 to 127 are defined to have
 424                                  * a one-byte argument.
 425                                  * Opcodes 128 to 159 are defined to have
 426                                  * an integer argument.
 427                                  */
 428                                 if (opcode > 0 && opcode < 128) {
 429                                         n_bytes += 1;
 430                                         (void) packet_get_char();
 431                                         break;
 432                                 } else if (opcode >= 128 && opcode < 160) {
 433                                         n_bytes += 4;
 434                                         (void) packet_get_int();
 435                                         break;
 436                                 } else {
 437                                         /*
 438                                          * It is a truly undefined opcode (160 to 255).
 439                                          * We have no idea about its arguments.  So we
 440                                          * must stop parsing.  Note that some data may be
 441                                          * left in the packet; hopefully there is nothing
 442                                          * more coming after the mode data.
 443                                          */
 444                                         log("parse_tty_modes: unknown opcode %d", opcode);
 445                                         goto set;
 446                                 }
 447                         } else {
 448                                 /*
 449                                  * SSH2:
 450                                  * Opcodes 1 to 159 are defined to have
 451                                  * a uint32 argument.
 452                                  * Opcodes 160 to 255 are undefined and
 453                                  * cause parsing to stop.
 454                                  */
 455                                 if (opcode > 0 && opcode < 160) {
 456                                         n_bytes += 4;
 457                                         (void) packet_get_int();
 458                                         break;
 459                                 } else {
 460                                         log("parse_tty_modes: unknown opcode %d", opcode);
 461                                         goto set;
 462                                 }
 463                         }
 464                 }
 465         }
 466 
 467 set:
 468         if (*n_bytes_ptr != n_bytes) {
 469                 *n_bytes_ptr = n_bytes;
 470                 log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
 471                     *n_bytes_ptr, n_bytes);
 472                 return;         /* Don't process bytes passed */
 473         }
 474         if (failure == -1)
 475                 return;         /* Packet parsed ok but tcgetattr() failed */
 476 
 477         /* Set the new modes for the terminal. */
 478         if (tcsetattr(fd, TCSANOW, &tio) == -1)
 479                 log("Setting tty modes failed: %.100s", strerror(errno));
 480 }