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