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 * Encoding and decoding of terminal modes in a portable way. 40 * Much of the format is defined in ttymodes.h; it is included multiple times 41 * into this file with the appropriate macro definitions to generate the 42 * suitable code. 43 */ 44 45 #include "includes.h" 46 RCSID("$OpenBSD: ttymodes.c,v 1.18 2002/06/19 00:27:55 deraadt Exp $"); 47 48 #include "packet.h" 49 #include "log.h" 50 #include "ssh1.h" 51 #include "compat.h" 52 #include "buffer.h" 53 #include "bufaux.h" 54 55 #define TTY_OP_END 0 56 /* 57 * uint32 (u_int) follows speed in SSH1 and SSH2 58 */ 59 #define TTY_OP_ISPEED_PROTO1 192 60 #define TTY_OP_OSPEED_PROTO1 193 61 #define TTY_OP_ISPEED_PROTO2 128 62 #define TTY_OP_OSPEED_PROTO2 129 63 64 /* 65 * Converts POSIX speed_t to a baud rate. The values of the 66 * constants for speed_t are not themselves portable. 67 */ 68 static int 69 speed_to_baud(speed_t speed) 70 { 71 switch (speed) { 72 case B0: 73 return 0; 74 case B50: 75 return 50; 76 case B75: 77 return 75; 78 case B110: 79 return 110; 80 case B134: 81 return 134; 82 case B150: 83 return 150; 84 case B200: 85 return 200; 86 case B300: 87 return 300; 88 case B600: 89 return 600; 90 case B1200: 91 return 1200; 92 case B1800: 93 return 1800; 94 case B2400: 95 return 2400; 96 case B4800: 97 return 4800; 98 case B9600: 99 return 9600; 100 101 #ifdef B19200 102 case B19200: 103 return 19200; 104 #else /* B19200 */ 105 #ifdef EXTA 106 case EXTA: 107 return 19200; 108 #endif /* EXTA */ 109 #endif /* B19200 */ 110 111 #ifdef B38400 112 case B38400: 113 return 38400; 114 #else /* B38400 */ 115 #ifdef EXTB 116 case EXTB: 117 return 38400; 118 #endif /* EXTB */ 119 #endif /* B38400 */ 120 121 #ifdef B7200 122 case B7200: 123 return 7200; 124 #endif /* B7200 */ 125 #ifdef B14400 126 case B14400: 127 return 14400; 128 #endif /* B14400 */ 129 #ifdef B28800 130 case B28800: 131 return 28800; 132 #endif /* B28800 */ 133 #ifdef B57600 134 case B57600: 135 return 57600; 136 #endif /* B57600 */ 137 #ifdef B76800 138 case B76800: 139 return 76800; 140 #endif /* B76800 */ 141 #ifdef B115200 142 case B115200: 143 return 115200; 144 #endif /* B115200 */ 145 #ifdef B230400 146 case B230400: 147 return 230400; 148 #endif /* B230400 */ 149 default: 150 return 9600; 151 } 152 } 153 154 /* 155 * Converts a numeric baud rate to a POSIX speed_t. 156 */ 157 static speed_t 158 baud_to_speed(int baud) 159 { 160 switch (baud) { 161 case 0: 162 return B0; 163 case 50: 164 return B50; 165 case 75: 166 return B75; 167 case 110: 168 return B110; 169 case 134: 170 return B134; 171 case 150: 172 return B150; 173 case 200: 174 return B200; 175 case 300: 176 return B300; 177 case 600: 178 return B600; 179 case 1200: 180 return B1200; 181 case 1800: 182 return B1800; 183 case 2400: 184 return B2400; 185 case 4800: 186 return B4800; 187 case 9600: 188 return B9600; 189 190 #ifdef B19200 191 case 19200: 192 return B19200; 193 #else /* B19200 */ 194 #ifdef EXTA 195 case 19200: 196 return EXTA; 197 #endif /* EXTA */ 198 #endif /* B19200 */ 199 200 #ifdef B38400 201 case 38400: 202 return B38400; 203 #else /* B38400 */ 204 #ifdef EXTB 205 case 38400: 206 return EXTB; 207 #endif /* EXTB */ 208 #endif /* B38400 */ 209 210 #ifdef B7200 211 case 7200: 212 return B7200; 213 #endif /* B7200 */ 214 #ifdef B14400 215 case 14400: 216 return B14400; 217 #endif /* B14400 */ 218 #ifdef B28800 219 case 28800: 220 return B28800; 221 #endif /* B28800 */ 222 #ifdef B57600 223 case 57600: 224 return B57600; 225 #endif /* B57600 */ 226 #ifdef B76800 227 case 76800: 228 return B76800; 229 #endif /* B76800 */ 230 #ifdef B115200 231 case 115200: 232 return B115200; 233 #endif /* B115200 */ 234 #ifdef B230400 235 case 230400: 236 return B230400; 237 #endif /* B230400 */ 238 default: 239 return B9600; 240 } 241 } 242 243 /* 244 * Encodes terminal modes for the terminal referenced by fd 245 * or tiop in a portable manner, and appends the modes to a packet 246 * being constructed. 247 */ 248 void 249 tty_make_modes(int fd, struct termios *tiop) 250 { 251 struct termios tio; 252 int baud; 253 Buffer buf; 254 int tty_op_ospeed, tty_op_ispeed; 255 void (*put_arg)(Buffer *, u_int); 256 257 buffer_init(&buf); 258 if (compat20) { 259 tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 260 tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 261 put_arg = buffer_put_int; 262 } else { 263 tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 264 tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 265 put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 266 } 267 268 if (tiop == NULL) { 269 if (tcgetattr(fd, &tio) == -1) { 270 log("tcgetattr: %.100s", strerror(errno)); 271 goto end; 272 } 273 } else 274 tio = *tiop; 275 276 /* Store input and output baud rates. */ 277 baud = speed_to_baud(cfgetospeed(&tio)); 278 debug3("tty_make_modes: ospeed %d", baud); 279 buffer_put_char(&buf, tty_op_ospeed); 280 buffer_put_int(&buf, baud); 281 baud = speed_to_baud(cfgetispeed(&tio)); 282 debug3("tty_make_modes: ispeed %d", baud); 283 buffer_put_char(&buf, tty_op_ispeed); 284 buffer_put_int(&buf, baud); 285 286 /* Store values of mode flags. */ 287 #define TTYCHAR(NAME, OP) \ 288 debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 289 buffer_put_char(&buf, OP); \ 290 put_arg(&buf, tio.c_cc[NAME]); 291 292 #define TTYMODE(NAME, FIELD, OP) \ 293 debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 294 buffer_put_char(&buf, OP); \ 295 put_arg(&buf, ((tio.FIELD & NAME) != 0)); 296 297 #include "ttymodes.h" 298 299 #undef TTYCHAR 300 #undef TTYMODE 301 302 end: 303 /* Mark end of mode data. */ 304 buffer_put_char(&buf, TTY_OP_END); 305 if (compat20) 306 packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 307 else 308 packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 309 buffer_free(&buf); 310 } 311 312 /* 313 * Decodes terminal modes for the terminal referenced by fd in a portable 314 * manner from a packet being read. 315 */ 316 void 317 tty_parse_modes(int fd, int *n_bytes_ptr) 318 { 319 struct termios tio; 320 int opcode, baud; 321 int n_bytes = 0; 322 int failure = 0; 323 u_int (*get_arg)(void); 324 int arg, arg_size; 325 326 if (compat20) { 327 *n_bytes_ptr = packet_get_int(); 328 debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 329 if (*n_bytes_ptr == 0) 330 return; 331 get_arg = packet_get_int; 332 arg_size = 4; 333 } else { 334 get_arg = packet_get_char; 335 arg_size = 1; 336 } 337 338 /* 339 * Get old attributes for the terminal. We will modify these 340 * flags. I am hoping that if there are any machine-specific 341 * modes, they will initially have reasonable values. 342 */ 343 if (tcgetattr(fd, &tio) == -1) { 344 log("tcgetattr: %.100s", strerror(errno)); 345 failure = -1; 346 } 347 348 for (;;) { 349 n_bytes += 1; 350 opcode = packet_get_char(); 351 switch (opcode) { 352 case TTY_OP_END: 353 goto set; 354 355 /* XXX: future conflict possible */ 356 case TTY_OP_ISPEED_PROTO1: 357 case TTY_OP_ISPEED_PROTO2: 358 n_bytes += 4; 359 baud = packet_get_int(); 360 debug3("tty_parse_modes: ispeed %d", baud); 361 if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1) 362 error("cfsetispeed failed for %d", baud); 363 break; 364 365 /* XXX: future conflict possible */ 366 case TTY_OP_OSPEED_PROTO1: 367 case TTY_OP_OSPEED_PROTO2: 368 n_bytes += 4; 369 baud = packet_get_int(); 370 debug3("tty_parse_modes: ospeed %d", baud); 371 if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1) 372 error("cfsetospeed failed for %d", baud); 373 break; 374 375 #define TTYCHAR(NAME, OP) \ 376 case OP: \ 377 n_bytes += arg_size; \ 378 tio.c_cc[NAME] = get_arg(); \ 379 debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 380 break; 381 #define TTYMODE(NAME, FIELD, OP) \ 382 case OP: \ 383 n_bytes += arg_size; \ 384 if ((arg = get_arg())) \ 385 tio.FIELD |= NAME; \ 386 else \ 387 tio.FIELD &= ~NAME; \ 388 debug3("tty_parse_modes: %d %d", OP, arg); \ 389 break; 390 391 #include "ttymodes.h" 392 393 #undef TTYCHAR 394 #undef TTYMODE 395 396 default: 397 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 398 opcode, opcode); 399 if (!compat20) { 400 /* 401 * SSH1: 402 * Opcodes 1 to 127 are defined to have 403 * a one-byte argument. 404 * Opcodes 128 to 159 are defined to have 405 * an integer argument. 406 */ 407 if (opcode > 0 && opcode < 128) { 408 n_bytes += 1; 409 (void) packet_get_char(); 410 break; 411 } else if (opcode >= 128 && opcode < 160) { 412 n_bytes += 4; 413 (void) packet_get_int(); 414 break; 415 } else { 416 /* 417 * It is a truly undefined opcode (160 to 255). 418 * We have no idea about its arguments. So we 419 * must stop parsing. Note that some data may be 420 * left in the packet; hopefully there is nothing 421 * more coming after the mode data. 422 */ 423 log("parse_tty_modes: unknown opcode %d", opcode); 424 goto set; 425 } 426 } else { 427 /* 428 * SSH2: 429 * Opcodes 1 to 159 are defined to have 430 * a uint32 argument. 431 * Opcodes 160 to 255 are undefined and 432 * cause parsing to stop. 433 */ 434 if (opcode > 0 && opcode < 160) { 435 n_bytes += 4; 436 (void) packet_get_int(); 437 break; 438 } else { 439 log("parse_tty_modes: unknown opcode %d", opcode); 440 goto set; 441 } 442 } 443 } 444 } 445 446 set: 447 if (*n_bytes_ptr != n_bytes) { 448 *n_bytes_ptr = n_bytes; 449 log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 450 *n_bytes_ptr, n_bytes); 451 return; /* Don't process bytes passed */ 452 } 453 if (failure == -1) 454 return; /* Packet parsed ok but tcgetattr() failed */ 455 456 /* Set the new modes for the terminal. */ 457 if (tcsetattr(fd, TCSANOW, &tio) == -1) 458 log("Setting tty modes failed: %.100s", strerror(errno)); 459 } 460