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