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 }