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