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 <sys/types.h> 47 48 #include <errno.h> 49 #include <string.h> 50 #include <termios.h> 51 #include <stdarg.h> 52 53 #include "packet.h" 54 #include "log.h" 55 #include "compat.h" 56 #include "sshbuf.h" 57 #include "ssherr.h" 58 59 #define TTY_OP_END 0 60 /* 61 * uint32 (u_int) follows speed. 62 */ 63 #define TTY_OP_ISPEED 128 64 #define TTY_OP_OSPEED 129 65 66 /* 67 * Converts POSIX speed_t to a baud rate. The values of the 68 * constants for speed_t are not themselves portable. 69 */ 70 static int 71 speed_to_baud(speed_t speed) 72 { 73 switch (speed) { 74 case B0: 75 return 0; 76 case B50: 77 return 50; 78 case B75: 79 return 75; 80 case B110: 81 return 110; 82 case B134: 83 return 134; 84 case B150: 85 return 150; 86 case B200: 87 return 200; 88 case B300: 89 return 300; 90 case B600: 91 return 600; 92 case B1200: 93 return 1200; 94 case B1800: 95 return 1800; 96 case B2400: 97 return 2400; 98 case B4800: 99 return 4800; 100 case B9600: 101 return 9600; 102 103 #ifdef B19200 104 case B19200: 105 return 19200; 106 #else /* B19200 */ 107 #ifdef EXTA 108 case EXTA: 109 return 19200; 110 #endif /* EXTA */ 111 #endif /* B19200 */ 112 113 #ifdef B38400 114 case B38400: 115 return 38400; 116 #else /* B38400 */ 117 #ifdef EXTB 118 case EXTB: 119 return 38400; 120 #endif /* EXTB */ 121 #endif /* B38400 */ 122 123 #ifdef B7200 124 case B7200: 125 return 7200; 126 #endif /* B7200 */ 127 #ifdef B14400 128 case B14400: 129 return 14400; 130 #endif /* B14400 */ 131 #ifdef B28800 132 case B28800: 133 return 28800; 134 #endif /* B28800 */ 135 #ifdef B57600 136 case B57600: 137 return 57600; 138 #endif /* B57600 */ 139 #ifdef B76800 140 case B76800: 141 return 76800; 142 #endif /* B76800 */ 143 #ifdef B115200 144 case B115200: 145 return 115200; 146 #endif /* B115200 */ 147 #ifdef B230400 148 case B230400: 149 return 230400; 150 #endif /* B230400 */ 151 default: 152 return 9600; 153 } 154 } 155 156 /* 157 * Converts a numeric baud rate to a POSIX speed_t. 158 */ 159 static speed_t 160 baud_to_speed(int baud) 161 { 162 switch (baud) { 163 case 0: 164 return B0; 165 case 50: 166 return B50; 167 case 75: 168 return B75; 169 case 110: 170 return B110; 171 case 134: 172 return B134; 173 case 150: 174 return B150; 175 case 200: 176 return B200; 177 case 300: 178 return B300; 179 case 600: 180 return B600; 181 case 1200: 182 return B1200; 183 case 1800: 184 return B1800; 185 case 2400: 186 return B2400; 187 case 4800: 188 return B4800; 189 case 9600: 190 return B9600; 191 192 #ifdef B19200 193 case 19200: 194 return B19200; 195 #else /* B19200 */ 196 #ifdef EXTA 197 case 19200: 198 return EXTA; 199 #endif /* EXTA */ 200 #endif /* B19200 */ 201 202 #ifdef B38400 203 case 38400: 204 return B38400; 205 #else /* B38400 */ 206 #ifdef EXTB 207 case 38400: 208 return EXTB; 209 #endif /* EXTB */ 210 #endif /* B38400 */ 211 212 #ifdef B7200 213 case 7200: 214 return B7200; 215 #endif /* B7200 */ 216 #ifdef B14400 217 case 14400: 218 return B14400; 219 #endif /* B14400 */ 220 #ifdef B28800 221 case 28800: 222 return B28800; 223 #endif /* B28800 */ 224 #ifdef B57600 225 case 57600: 226 return B57600; 227 #endif /* B57600 */ 228 #ifdef B76800 229 case 76800: 230 return B76800; 231 #endif /* B76800 */ 232 #ifdef B115200 233 case 115200: 234 return B115200; 235 #endif /* B115200 */ 236 #ifdef B230400 237 case 230400: 238 return B230400; 239 #endif /* B230400 */ 240 default: 241 return B9600; 242 } 243 } 244 245 /* 246 * Encodes terminal modes for the terminal referenced by fd 247 * or tiop in a portable manner, and appends the modes to a packet 248 * being constructed. 249 */ 250 void 251 ssh_tty_make_modes(struct ssh *ssh, int fd, struct termios *tiop) 252 { 253 struct termios tio; 254 struct sshbuf *buf; 255 int r, ibaud, obaud; 256 257 if ((buf = sshbuf_new()) == NULL) 258 fatal("%s: sshbuf_new failed", __func__); 259 260 if (tiop == NULL) { 261 if (fd == -1) { 262 debug("%s: no fd or tio", __func__); 263 goto end; 264 } 265 if (tcgetattr(fd, &tio) == -1) { 266 logit("tcgetattr: %.100s", strerror(errno)); 267 goto end; 268 } 269 } else 270 tio = *tiop; 271 272 /* Store input and output baud rates. */ 273 obaud = speed_to_baud(cfgetospeed(&tio)); 274 ibaud = speed_to_baud(cfgetispeed(&tio)); 275 if ((r = sshbuf_put_u8(buf, TTY_OP_OSPEED)) != 0 || 276 (r = sshbuf_put_u32(buf, obaud)) != 0 || 277 (r = sshbuf_put_u8(buf, TTY_OP_ISPEED)) != 0 || 278 (r = sshbuf_put_u32(buf, ibaud)) != 0) 279 fatal("%s: buffer error: %s", __func__, ssh_err(r)); 280 281 /* Store values of mode flags. */ 282 #define TTYCHAR(NAME, OP) \ 283 if ((r = sshbuf_put_u8(buf, OP)) != 0 || \ 284 (r = sshbuf_put_u32(buf, tio.c_cc[NAME])) != 0) \ 285 fatal("%s: buffer error: %s", __func__, ssh_err(r)); \ 286 287 #define SSH_TTYMODE_IUTF8 42 /* for SSH_BUG_UTF8TTYMODE */ 288 289 #define TTYMODE(NAME, FIELD, OP) \ 290 if (OP == SSH_TTYMODE_IUTF8 && (datafellows & SSH_BUG_UTF8TTYMODE)) { \ 291 debug3("%s: SSH_BUG_UTF8TTYMODE", __func__); \ 292 } else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \ 293 (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \ 294 fatal("%s: buffer error: %s", __func__, ssh_err(r)); \ 295 296 #include "ttymodes.h" 297 298 #undef TTYCHAR 299 #undef TTYMODE 300 301 end: 302 /* Mark end of mode data. */ 303 if ((r = sshbuf_put_u8(buf, TTY_OP_END)) != 0 || 304 (r = sshpkt_put_stringb(ssh, buf)) != 0) 305 fatal("%s: packet error: %s", __func__, ssh_err(r)); 306 sshbuf_free(buf); 307 } 308 309 /* 310 * Decodes terminal modes for the terminal referenced by fd in a portable 311 * manner from a packet being read. 312 */ 313 void 314 ssh_tty_parse_modes(struct ssh *ssh, int fd) 315 { 316 struct termios tio; 317 struct sshbuf *buf; 318 const u_char *data; 319 u_char opcode; 320 u_int baud, u; 321 int r, failure = 0; 322 size_t len; 323 324 if ((r = sshpkt_get_string_direct(ssh, &data, &len)) != 0) 325 fatal("%s: packet error: %s", __func__, ssh_err(r)); 326 if (len == 0) 327 return; 328 if ((buf = sshbuf_from(data, len)) == NULL) { 329 error("%s: sshbuf_from failed", __func__); 330 return; 331 } 332 333 /* 334 * Get old attributes for the terminal. We will modify these 335 * flags. I am hoping that if there are any machine-specific 336 * modes, they will initially have reasonable values. 337 */ 338 if (tcgetattr(fd, &tio) == -1) { 339 logit("tcgetattr: %.100s", strerror(errno)); 340 failure = -1; 341 } 342 343 while (sshbuf_len(buf) > 0) { 344 if ((r = sshbuf_get_u8(buf, &opcode)) != 0) 345 fatal("%s: packet error: %s", __func__, ssh_err(r)); 346 switch (opcode) { 347 case TTY_OP_END: 348 goto set; 349 350 case TTY_OP_ISPEED: 351 if ((r = sshbuf_get_u32(buf, &baud)) != 0) 352 fatal("%s: packet error: %s", 353 __func__, ssh_err(r)); 354 if (failure != -1 && 355 cfsetispeed(&tio, baud_to_speed(baud)) == -1) 356 error("cfsetispeed failed for %d", baud); 357 break; 358 359 case TTY_OP_OSPEED: 360 if ((r = sshbuf_get_u32(buf, &baud)) != 0) 361 fatal("%s: packet error: %s", 362 __func__, ssh_err(r)); 363 if (failure != -1 && 364 cfsetospeed(&tio, baud_to_speed(baud)) == -1) 365 error("cfsetospeed failed for %d", baud); 366 break; 367 368 #define TTYCHAR(NAME, OP) \ 369 case OP: \ 370 if ((r = sshbuf_get_u32(buf, &u)) != 0) \ 371 fatal("%s: packet error: %s", __func__, \ 372 ssh_err(r)); \ 373 tio.c_cc[NAME] = u; \ 374 break; 375 #define TTYMODE(NAME, FIELD, OP) \ 376 case OP: \ 377 if ((r = sshbuf_get_u32(buf, &u)) != 0) \ 378 fatal("%s: packet error: %s", __func__, \ 379 ssh_err(r)); \ 380 if (u) \ 381 tio.FIELD |= NAME; \ 382 else \ 383 tio.FIELD &= ~NAME; \ 384 break; 385 386 #include "ttymodes.h" 387 388 #undef TTYCHAR 389 #undef TTYMODE 390 391 default: 392 debug("Ignoring unsupported tty mode opcode %d (0x%x)", 393 opcode, opcode); 394 /* 395 * SSH2: 396 * Opcodes 1 to 159 are defined to have a uint32 397 * argument. 398 * Opcodes 160 to 255 are undefined and cause parsing 399 * to stop. 400 */ 401 if (opcode > 0 && opcode < 160) { 402 if ((r = sshbuf_get_u32(buf, NULL)) != 0) 403 fatal("%s: packet error: %s", __func__, 404 ssh_err(r)); 405 break; 406 } else { 407 logit("%s: unknown opcode %d", __func__, 408 opcode); 409 goto set; 410 } 411 } 412 } 413 414 set: 415 len = sshbuf_len(buf); 416 sshbuf_free(buf); 417 if (len > 0) { 418 logit("%s: %zu bytes left", __func__, len); 419 return; /* Don't process bytes passed */ 420 } 421 if (failure == -1) 422 return; /* Packet parsed ok but tcgetattr() failed */ 423 424 /* Set the new modes for the terminal. */ 425 if (tcsetattr(fd, TCSANOW, &tio) == -1) 426 logit("Setting tty modes failed: %.100s", strerror(errno)); 427 } 428