ttymodes.c revision 162853
1223013Sdim/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */ 2223013Sdim/* 3223013Sdim * Author: Tatu Ylonen <ylo@cs.hut.fi> 4223013Sdim * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 5223013Sdim * All rights reserved 6223013Sdim * 7223013Sdim * As far as I am concerned, the code I have written for this software 8223013Sdim * can be used freely for any purpose. Any derived versions of this 9223013Sdim * software must be clearly marked as such, and if the derived work is 10223013Sdim * incompatible with the protocol description in the RFC file, it must be 11223013Sdim * called by a name other than "ssh" or "Secure Shell". 12223013Sdim */ 13223013Sdim 14223013Sdim/* 15223013Sdim * SSH2 tty modes support by Kevin Steves. 16223013Sdim * Copyright (c) 2001 Kevin Steves. All rights reserved. 17223013Sdim * 18243830Sdim * Redistribution and use in source and binary forms, with or without 19223013Sdim * modification, are permitted provided that the following conditions 20243830Sdim * are met: 21234353Sdim * 1. Redistributions of source code must retain the above copyright 22223013Sdim * notice, this list of conditions and the following disclaimer. 23223013Sdim * 2. Redistributions in binary form must reproduce the above copyright 24249423Sdim * notice, this list of conditions and the following disclaimer in the 25223013Sdim * documentation and/or other materials provided with the distribution. 26223013Sdim * 27223013Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 28234353Sdim * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 29234353Sdim * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 30234353Sdim * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 31234353Sdim * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 32223013Sdim * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33223013Sdim * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34223013Sdim * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35223013Sdim * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 36223013Sdim * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 37223013Sdim */ 38223013Sdim 39223013Sdim/* 40223013Sdim * Encoding and decoding of terminal modes in a portable way. 41223013Sdim * Much of the format is defined in ttymodes.h; it is included multiple times 42223013Sdim * into this file with the appropriate macro definitions to generate the 43263508Sdim * suitable code. 44263508Sdim */ 45263508Sdim 46223013Sdim#include "includes.h" 47223013Sdim 48223013Sdim#include <sys/types.h> 49223013Sdim 50249423Sdim#include <errno.h> 51223013Sdim#include <string.h> 52223013Sdim#include <termios.h> 53223013Sdim#include <stdarg.h> 54223013Sdim 55223013Sdim#include "packet.h" 56223013Sdim#include "log.h" 57239462Sdim#include "ssh1.h" 58239462Sdim#include "compat.h" 59223013Sdim#include "buffer.h" 60223013Sdim 61223013Sdim#define TTY_OP_END 0 62223013Sdim/* 63223013Sdim * uint32 (u_int) follows speed in SSH1 and SSH2 64243830Sdim */ 65243830Sdim#define TTY_OP_ISPEED_PROTO1 192 66223013Sdim#define TTY_OP_OSPEED_PROTO1 193 67243830Sdim#define TTY_OP_ISPEED_PROTO2 128 68243830Sdim#define TTY_OP_OSPEED_PROTO2 129 69223013Sdim 70223013Sdim/* 71223013Sdim * Converts POSIX speed_t to a baud rate. The values of the 72223013Sdim * constants for speed_t are not themselves portable. 73223013Sdim */ 74223013Sdimstatic int 75223013Sdimspeed_to_baud(speed_t speed) 76223013Sdim{ 77223013Sdim switch (speed) { 78223013Sdim case B0: 79223013Sdim return 0; 80223013Sdim case B50: 81223013Sdim return 50; 82223013Sdim case B75: 83223013Sdim return 75; 84223013Sdim case B110: 85249423Sdim return 110; 86223013Sdim case B134: 87223013Sdim return 134; 88249423Sdim case B150: 89249423Sdim return 150; 90249423Sdim case B200: 91249423Sdim return 200; 92223013Sdim case B300: 93223013Sdim return 300; 94223013Sdim case B600: 95249423Sdim return 600; 96224145Sdim case B1200: 97224145Sdim return 1200; 98223013Sdim case B1800: 99223013Sdim return 1800; 100223013Sdim case B2400: 101249423Sdim return 2400; 102249423Sdim case B4800: 103249423Sdim return 4800; 104223013Sdim case B9600: 105223013Sdim return 9600; 106223013Sdim 107249423Sdim#ifdef B19200 108249423Sdim case B19200: 109249423Sdim return 19200; 110223013Sdim#else /* B19200 */ 111249423Sdim#ifdef EXTA 112249423Sdim case EXTA: 113223013Sdim return 19200; 114223013Sdim#endif /* EXTA */ 115223013Sdim#endif /* B19200 */ 116223013Sdim 117223013Sdim#ifdef B38400 118249423Sdim case B38400: 119249423Sdim return 38400; 120249423Sdim#else /* B38400 */ 121249423Sdim#ifdef EXTB 122249423Sdim case EXTB: 123249423Sdim return 38400; 124249423Sdim#endif /* EXTB */ 125249423Sdim#endif /* B38400 */ 126223013Sdim 127234353Sdim#ifdef B7200 128234353Sdim case B7200: 129234353Sdim return 7200; 130234353Sdim#endif /* B7200 */ 131226633Sdim#ifdef B14400 132226633Sdim case B14400: 133226633Sdim return 14400; 134226633Sdim#endif /* B14400 */ 135226633Sdim#ifdef B28800 136249423Sdim case B28800: 137249423Sdim return 28800; 138249423Sdim#endif /* B28800 */ 139223013Sdim#ifdef B57600 140223013Sdim case B57600: 141224145Sdim return 57600; 142223013Sdim#endif /* B57600 */ 143226633Sdim#ifdef B76800 144223013Sdim case B76800: 145223013Sdim return 76800; 146223013Sdim#endif /* B76800 */ 147223013Sdim#ifdef B115200 148223013Sdim case B115200: 149223013Sdim return 115200; 150263508Sdim#endif /* B115200 */ 151263508Sdim#ifdef B230400 152263508Sdim case B230400: 153263508Sdim return 230400; 154263508Sdim#endif /* B230400 */ 155263508Sdim default: 156263508Sdim return 9600; 157263508Sdim } 158263508Sdim} 159263508Sdim 160263508Sdim/* 161263508Sdim * Converts a numeric baud rate to a POSIX speed_t. 162263508Sdim */ 163263508Sdimstatic speed_t 164263508Sdimbaud_to_speed(int baud) 165263508Sdim{ 166263508Sdim switch (baud) { 167263508Sdim case 0: 168263508Sdim return B0; 169263508Sdim case 50: 170263508Sdim return B50; 171263508Sdim case 75: 172263508Sdim return B75; 173263508Sdim case 110: 174263508Sdim return B110; 175263508Sdim case 134: 176263508Sdim return B134; 177263508Sdim case 150: 178263508Sdim return B150; 179 case 200: 180 return B200; 181 case 300: 182 return B300; 183 case 600: 184 return B600; 185 case 1200: 186 return B1200; 187 case 1800: 188 return B1800; 189 case 2400: 190 return B2400; 191 case 4800: 192 return B4800; 193 case 9600: 194 return B9600; 195 196#ifdef B19200 197 case 19200: 198 return B19200; 199#else /* B19200 */ 200#ifdef EXTA 201 case 19200: 202 return EXTA; 203#endif /* EXTA */ 204#endif /* B19200 */ 205 206#ifdef B38400 207 case 38400: 208 return B38400; 209#else /* B38400 */ 210#ifdef EXTB 211 case 38400: 212 return EXTB; 213#endif /* EXTB */ 214#endif /* B38400 */ 215 216#ifdef B7200 217 case 7200: 218 return B7200; 219#endif /* B7200 */ 220#ifdef B14400 221 case 14400: 222 return B14400; 223#endif /* B14400 */ 224#ifdef B28800 225 case 28800: 226 return B28800; 227#endif /* B28800 */ 228#ifdef B57600 229 case 57600: 230 return B57600; 231#endif /* B57600 */ 232#ifdef B76800 233 case 76800: 234 return B76800; 235#endif /* B76800 */ 236#ifdef B115200 237 case 115200: 238 return B115200; 239#endif /* B115200 */ 240#ifdef B230400 241 case 230400: 242 return B230400; 243#endif /* B230400 */ 244 default: 245 return B9600; 246 } 247} 248 249/* 250 * Encode a special character into SSH line format. 251 */ 252static u_int 253special_char_encode(cc_t c) 254{ 255#ifdef _POSIX_VDISABLE 256 if (c == _POSIX_VDISABLE) 257 return 255; 258#endif /* _POSIX_VDISABLE */ 259 return c; 260} 261 262/* 263 * Decode a special character from SSH line format. 264 */ 265static cc_t 266special_char_decode(u_int c) 267{ 268#ifdef _POSIX_VDISABLE 269 if (c == 255) 270 return _POSIX_VDISABLE; 271#endif /* _POSIX_VDISABLE */ 272 return c; 273} 274 275/* 276 * Encodes terminal modes for the terminal referenced by fd 277 * or tiop in a portable manner, and appends the modes to a packet 278 * being constructed. 279 */ 280void 281tty_make_modes(int fd, struct termios *tiop) 282{ 283 struct termios tio; 284 int baud; 285 Buffer buf; 286 int tty_op_ospeed, tty_op_ispeed; 287 void (*put_arg)(Buffer *, u_int); 288 289 buffer_init(&buf); 290 if (compat20) { 291 tty_op_ospeed = TTY_OP_OSPEED_PROTO2; 292 tty_op_ispeed = TTY_OP_ISPEED_PROTO2; 293 put_arg = buffer_put_int; 294 } else { 295 tty_op_ospeed = TTY_OP_OSPEED_PROTO1; 296 tty_op_ispeed = TTY_OP_ISPEED_PROTO1; 297 put_arg = (void (*)(Buffer *, u_int)) buffer_put_char; 298 } 299 300 if (tiop == NULL) { 301 if (tcgetattr(fd, &tio) == -1) { 302 logit("tcgetattr: %.100s", strerror(errno)); 303 goto end; 304 } 305 } else 306 tio = *tiop; 307 308 /* Store input and output baud rates. */ 309 baud = speed_to_baud(cfgetospeed(&tio)); 310 debug3("tty_make_modes: ospeed %d", baud); 311 buffer_put_char(&buf, tty_op_ospeed); 312 buffer_put_int(&buf, baud); 313 baud = speed_to_baud(cfgetispeed(&tio)); 314 debug3("tty_make_modes: ispeed %d", baud); 315 buffer_put_char(&buf, tty_op_ispeed); 316 buffer_put_int(&buf, baud); 317 318 /* Store values of mode flags. */ 319#define TTYCHAR(NAME, OP) \ 320 debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \ 321 buffer_put_char(&buf, OP); \ 322 put_arg(&buf, special_char_encode(tio.c_cc[NAME])); 323 324#define TTYMODE(NAME, FIELD, OP) \ 325 debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \ 326 buffer_put_char(&buf, OP); \ 327 put_arg(&buf, ((tio.FIELD & NAME) != 0)); 328 329#include "ttymodes.h" 330 331#undef TTYCHAR 332#undef TTYMODE 333 334end: 335 /* Mark end of mode data. */ 336 buffer_put_char(&buf, TTY_OP_END); 337 if (compat20) 338 packet_put_string(buffer_ptr(&buf), buffer_len(&buf)); 339 else 340 packet_put_raw(buffer_ptr(&buf), buffer_len(&buf)); 341 buffer_free(&buf); 342} 343 344/* 345 * Decodes terminal modes for the terminal referenced by fd in a portable 346 * manner from a packet being read. 347 */ 348void 349tty_parse_modes(int fd, int *n_bytes_ptr) 350{ 351 struct termios tio; 352 int opcode, baud; 353 int n_bytes = 0; 354 int failure = 0; 355 u_int (*get_arg)(void); 356 int arg, arg_size; 357 358 if (compat20) { 359 *n_bytes_ptr = packet_get_int(); 360 debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr); 361 if (*n_bytes_ptr == 0) 362 return; 363 get_arg = packet_get_int; 364 arg_size = 4; 365 } else { 366 get_arg = packet_get_char; 367 arg_size = 1; 368 } 369 370 /* 371 * Get old attributes for the terminal. We will modify these 372 * flags. I am hoping that if there are any machine-specific 373 * modes, they will initially have reasonable values. 374 */ 375 if (tcgetattr(fd, &tio) == -1) { 376 logit("tcgetattr: %.100s", strerror(errno)); 377 failure = -1; 378 } 379 380 for (;;) { 381 n_bytes += 1; 382 opcode = packet_get_char(); 383 switch (opcode) { 384 case TTY_OP_END: 385 goto set; 386 387 /* XXX: future conflict possible */ 388 case TTY_OP_ISPEED_PROTO1: 389 case TTY_OP_ISPEED_PROTO2: 390 n_bytes += 4; 391 baud = packet_get_int(); 392 debug3("tty_parse_modes: ispeed %d", baud); 393 if (failure != -1 && 394 cfsetispeed(&tio, baud_to_speed(baud)) == -1) 395 error("cfsetispeed failed for %d", baud); 396 break; 397 398 /* XXX: future conflict possible */ 399 case TTY_OP_OSPEED_PROTO1: 400 case TTY_OP_OSPEED_PROTO2: 401 n_bytes += 4; 402 baud = packet_get_int(); 403 debug3("tty_parse_modes: ospeed %d", baud); 404 if (failure != -1 && 405 cfsetospeed(&tio, baud_to_speed(baud)) == -1) 406 error("cfsetospeed failed for %d", baud); 407 break; 408 409#define TTYCHAR(NAME, OP) \ 410 case OP: \ 411 n_bytes += arg_size; \ 412 tio.c_cc[NAME] = special_char_decode(get_arg()); \ 413 debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \ 414 break; 415#define TTYMODE(NAME, FIELD, OP) \ 416 case OP: \ 417 n_bytes += arg_size; \ 418 if ((arg = get_arg())) \ 419 tio.FIELD |= NAME; \ 420 else \ 421 tio.FIELD &= ~NAME; \ 422 debug3("tty_parse_modes: %d %d", OP, arg); \ 423 break; 424 425#include "ttymodes.h" 426 427#undef TTYCHAR 428#undef TTYMODE 429 430 default: 431 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 432 opcode, opcode); 433 if (!compat20) { 434 /* 435 * SSH1: 436 * Opcodes 1 to 127 are defined to have 437 * a one-byte argument. 438 * Opcodes 128 to 159 are defined to have 439 * an integer argument. 440 */ 441 if (opcode > 0 && opcode < 128) { 442 n_bytes += 1; 443 (void) packet_get_char(); 444 break; 445 } else if (opcode >= 128 && opcode < 160) { 446 n_bytes += 4; 447 (void) packet_get_int(); 448 break; 449 } else { 450 /* 451 * It is a truly undefined opcode (160 to 255). 452 * We have no idea about its arguments. So we 453 * must stop parsing. Note that some data 454 * may be left in the packet; hopefully there 455 * is nothing more coming after the mode data. 456 */ 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