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.13 2001/04/15 01:35:22 stevesk 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 debug2("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 debug2("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 debug2("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 debug2("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 return; 311 } 312 313 /* 314 * Decodes terminal modes for the terminal referenced by fd in a portable 315 * manner from a packet being read. 316 */ 317 void 318 tty_parse_modes(int fd, int *n_bytes_ptr) 319 { 320 struct termios tio; 321 int opcode, baud; 322 int n_bytes = 0; 323 int failure = 0; 324 u_int (*get_arg)(void); 325 int arg, arg_size; 326 327 if (compat20) { 328 *n_bytes_ptr = packet_get_int(); 329 debug2("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 330 if (*n_bytes_ptr == 0) 331 return; 332 get_arg = packet_get_int; 333 arg_size = 4; 334 } else { 335 get_arg = packet_get_char; 336 arg_size = 1; 337 } 338 339 /* 340 * Get old attributes for the terminal. We will modify these 341 * flags. I am hoping that if there are any machine-specific 342 * modes, they will initially have reasonable values. 343 */ 344 if (tcgetattr(fd, &tio) == -1) { 345 log("tcgetattr: %.100s", strerror(errno)); 346 failure = -1; 347 } 348 349 for (;;) { 350 n_bytes += 1; 351 opcode = packet_get_char(); 352 switch (opcode) { 353 case TTY_OP_END: 354 goto set; 355 356 /* XXX: future conflict possible */ 357 case TTY_OP_ISPEED_PROTO1: 358 case TTY_OP_ISPEED_PROTO2: 359 n_bytes += 4; 360 baud = packet_get_int(); 361 debug2("tty_parse_modes: ispeed %d", baud); 362 if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1) 363 error("cfsetispeed failed for %d", baud); 364 break; 365 366 /* XXX: future conflict possible */ 367 case TTY_OP_OSPEED_PROTO1: 368 case TTY_OP_OSPEED_PROTO2: 369 n_bytes += 4; 370 baud = packet_get_int(); 371 debug2("tty_parse_modes: ospeed %d", baud); 372 if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1) 373 error("cfsetospeed failed for %d", baud); 374 break; 375 376 #define TTYCHAR(NAME, OP) \ 377 case OP: \ 378 n_bytes += arg_size; \ 379 tio.c_cc[NAME] = get_arg(); \ 380 debug2("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 381 break; 382 #define TTYMODE(NAME, FIELD, OP) \ 383 case OP: \ 384 n_bytes += arg_size; \ 385 if ((arg = get_arg())) \ 386 tio.FIELD |= NAME; \ 387 else \ 388 tio.FIELD &= ~NAME; \ 389 debug2("tty_parse_modes: %d %d", OP, arg); \ 390 break; 391 392 #include "ttymodes.h" 393 394 #undef TTYCHAR 395 #undef TTYMODE 396 397 default: 398 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 399 opcode, opcode); 400 if (!compat20) { 401 /* 402 * SSH1: 403 * Opcodes 1 to 127 are defined to have 404 * a one-byte argument. 405 * Opcodes 128 to 159 are defined to have 406 * an integer argument. 407 */ 408 if (opcode > 0 && opcode < 128) { 409 n_bytes += 1; 410 (void) packet_get_char(); 411 break; 412 } else if (opcode >= 128 && opcode < 160) { 413 n_bytes += 4; 414 (void) packet_get_int(); 415 break; 416 } else { 417 /* 418 * It is a truly undefined opcode (160 to 255). 419 * We have no idea about its arguments. So we 420 * must stop parsing. Note that some data may be 421 * left in the packet; hopefully there is nothing 422 * more coming after the mode data. 423 */ 424 log("parse_tty_modes: unknown opcode %d", opcode); 425 packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); 426 goto set; 427 } 428 } else { 429 /* 430 * SSH2: 431 * Opcodes 1 to 159 are defined to have 432 * a uint32 argument. 433 * Opcodes 160 to 255 are undefined and 434 * cause parsing to stop. 435 */ 436 if (opcode > 0 && opcode < 160) { 437 n_bytes += 4; 438 (void) packet_get_int(); 439 break; 440 } else { 441 log("parse_tty_modes: unknown opcode %d", opcode); 442 goto set; 443 } 444 } 445 } 446 } 447 448 set: 449 if (*n_bytes_ptr != n_bytes) { 450 *n_bytes_ptr = n_bytes; 451 log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d", 452 *n_bytes_ptr, n_bytes); 453 return; /* Don't process bytes passed */ 454 } 455 if (failure == -1) 456 return; /* Packet parsed ok but tcgetattr() failed */ 457 458 /* Set the new modes for the terminal. */ 459 if (tcsetattr(fd, TCSANOW, &tio) == -1) 460 log("Setting tty modes failed: %.100s", strerror(errno)); 461 return; 462 } 463