Home | History | Annotate | Download | only in common
      1 /*
      2  * Author: Tatu Ylonen <ylo (at) cs.hut.fi>
      3  * Copyright (c) 1995 Tatu Ylonen <ylo (at) cs.hut.fi>, Espoo, Finland
      4  *                    All rights reserved
      5  *
      6  * As far as I am concerned, the code I have written for this software
      7  * can be used freely for any purpose.  Any derived versions of this
      8  * software must be clearly marked as such, and if the derived work is
      9  * incompatible with the protocol description in the RFC file, it must be
     10  * called by a name other than "ssh" or "Secure Shell".
     11  */
     12 
     13 /*
     14  * SSH2 tty modes support by Kevin Steves.
     15  * Copyright (c) 2001 Kevin Steves.  All rights reserved.
     16  *
     17  * Redistribution and use in source and binary forms, with or without
     18  * modification, are permitted provided that the following conditions
     19  * are met:
     20  * 1. Redistributions of source code must retain the above copyright
     21  *    notice, this list of conditions and the following disclaimer.
     22  * 2. Redistributions in binary form must reproduce the above copyright
     23  *    notice, this list of conditions and the following disclaimer in the
     24  *    documentation and/or other materials provided with the distribution.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     36  */
     37 /*
     38  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
     39  * Use is subject to license terms.
     40  */
     41 
     42 /*
     43  * Encoding and decoding of terminal modes in a portable way.
     44  * Much of the format is defined in ttymodes.h; it is included multiple times
     45  * into this file with the appropriate macro definitions to generate the
     46  * suitable code.
     47  */
     48 
     49 #include "includes.h"
     50 RCSID("$OpenBSD: ttymodes.c,v 1.18 2002/06/19 00:27:55 deraadt Exp $");
     51 
     52 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     53 
     54 #include "packet.h"
     55 #include "log.h"
     56 #include "ssh1.h"
     57 #include "compat.h"
     58 #include "buffer.h"
     59 #include "bufaux.h"
     60 
     61 #define TTY_OP_END		0
     62 /*
     63  * uint32 (u_int) follows speed in SSH1 and SSH2
     64  */
     65 #define TTY_OP_ISPEED_PROTO1	192
     66 #define TTY_OP_OSPEED_PROTO1	193
     67 #define TTY_OP_ISPEED_PROTO2	128
     68 #define TTY_OP_OSPEED_PROTO2	129
     69 
     70 /*
     71  * Converts POSIX speed_t to a baud rate.  The values of the
     72  * constants for speed_t are not themselves portable.
     73  */
     74 static int
     75 speed_to_baud(speed_t speed)
     76 {
     77 	switch (speed) {
     78 	case B0:
     79 		return 0;
     80 	case B50:
     81 		return 50;
     82 	case B75:
     83 		return 75;
     84 	case B110:
     85 		return 110;
     86 	case B134:
     87 		return 134;
     88 	case B150:
     89 		return 150;
     90 	case B200:
     91 		return 200;
     92 	case B300:
     93 		return 300;
     94 	case B600:
     95 		return 600;
     96 	case B1200:
     97 		return 1200;
     98 	case B1800:
     99 		return 1800;
    100 	case B2400:
    101 		return 2400;
    102 	case B4800:
    103 		return 4800;
    104 	case B9600:
    105 		return 9600;
    106 
    107 #ifdef B19200
    108 	case B19200:
    109 		return 19200;
    110 #else /* B19200 */
    111 #ifdef EXTA
    112 	case EXTA:
    113 		return 19200;
    114 #endif /* EXTA */
    115 #endif /* B19200 */
    116 
    117 #ifdef B38400
    118 	case B38400:
    119 		return 38400;
    120 #else /* B38400 */
    121 #ifdef EXTB
    122 	case EXTB:
    123 		return 38400;
    124 #endif /* EXTB */
    125 #endif /* B38400 */
    126 
    127 #ifdef B7200
    128 	case B7200:
    129 		return 7200;
    130 #endif /* B7200 */
    131 #ifdef B14400
    132 	case B14400:
    133 		return 14400;
    134 #endif /* B14400 */
    135 #ifdef B28800
    136 	case B28800:
    137 		return 28800;
    138 #endif /* B28800 */
    139 #ifdef B57600
    140 	case B57600:
    141 		return 57600;
    142 #endif /* B57600 */
    143 #ifdef B76800
    144 	case B76800:
    145 		return 76800;
    146 #endif /* B76800 */
    147 #ifdef B115200
    148 	case B115200:
    149 		return 115200;
    150 #endif /* B115200 */
    151 #ifdef B230400
    152 	case B230400:
    153 		return 230400;
    154 #endif /* B230400 */
    155 	default:
    156 		return 9600;
    157 	}
    158 }
    159 
    160 /*
    161  * Converts a numeric baud rate to a POSIX speed_t.
    162  */
    163 static speed_t
    164 baud_to_speed(int baud)
    165 {
    166 	switch (baud) {
    167 	case 0:
    168 		return B0;
    169 	case 50:
    170 		return B50;
    171 	case 75:
    172 		return B75;
    173 	case 110:
    174 		return B110;
    175 	case 134:
    176 		return B134;
    177 	case 150:
    178 		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  * Encodes terminal modes for the terminal referenced by fd
    251  * or tiop in a portable manner, and appends the modes to a packet
    252  * being constructed.
    253  */
    254 void
    255 tty_make_modes(int fd, struct termios *tiop)
    256 {
    257 	struct termios tio;
    258 	int baud;
    259 	Buffer buf;
    260 	int tty_op_ospeed, tty_op_ispeed;
    261 	void (*put_arg)(Buffer *, u_int);
    262 
    263 	buffer_init(&buf);
    264 	if (compat20) {
    265 		tty_op_ospeed = TTY_OP_OSPEED_PROTO2;
    266 		tty_op_ispeed = TTY_OP_ISPEED_PROTO2;
    267 		put_arg = buffer_put_int;
    268 	} else {
    269 		tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
    270 		tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
    271 		put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
    272 	}
    273 
    274 	if (tiop == NULL) {
    275 		if (tcgetattr(fd, &tio) == -1) {
    276 			log("tcgetattr: %.100s", strerror(errno));
    277 			goto end;
    278 		}
    279 	} else
    280 		tio = *tiop;
    281 
    282 	/* Store input and output baud rates. */
    283 	baud = speed_to_baud(cfgetospeed(&tio));
    284 	debug3("tty_make_modes: ospeed %d", baud);
    285 	buffer_put_char(&buf, tty_op_ospeed);
    286 	buffer_put_int(&buf, baud);
    287 	baud = speed_to_baud(cfgetispeed(&tio));
    288 	debug3("tty_make_modes: ispeed %d", baud);
    289 	buffer_put_char(&buf, tty_op_ispeed);
    290 	buffer_put_int(&buf, baud);
    291 
    292 	/* Store values of mode flags. */
    293 #define TTYCHAR(NAME, OP) \
    294 	debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \
    295 	buffer_put_char(&buf, OP); \
    296 	put_arg(&buf, tio.c_cc[NAME]);
    297 
    298 #define TTYMODE(NAME, FIELD, OP) \
    299 	debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \
    300 	buffer_put_char(&buf, OP); \
    301 	put_arg(&buf, ((tio.FIELD & NAME) != 0));
    302 
    303 #include "ttymodes.h"
    304 
    305 #undef TTYCHAR
    306 #undef TTYMODE
    307 
    308 end:
    309 	/* Mark end of mode data. */
    310 	buffer_put_char(&buf, TTY_OP_END);
    311 	if (compat20)
    312 		packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
    313 	else
    314 		packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
    315 	buffer_free(&buf);
    316 }
    317 
    318 /*
    319  * Decodes terminal modes for the terminal referenced by fd in a portable
    320  * manner from a packet being read.
    321  */
    322 void
    323 tty_parse_modes(int fd, int *n_bytes_ptr)
    324 {
    325 	struct termios tio;
    326 	int opcode, baud;
    327 	int n_bytes = 0;
    328 	int failure = 0;
    329 	u_int (*get_arg)(void);
    330 	int arg, arg_size;
    331 
    332 	if (compat20) {
    333 		*n_bytes_ptr = packet_get_int();
    334 		debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr);
    335 		if (*n_bytes_ptr == 0)
    336 			return;
    337 		get_arg = packet_get_int;
    338 		arg_size = 4;
    339 	} else {
    340 		get_arg = packet_get_char;
    341 		arg_size = 1;
    342 	}
    343 
    344 	/*
    345 	 * Get old attributes for the terminal.  We will modify these
    346 	 * flags. I am hoping that if there are any machine-specific
    347 	 * modes, they will initially have reasonable values.
    348 	 */
    349 	if (tcgetattr(fd, &tio) == -1) {
    350 		log("tcgetattr: %.100s", strerror(errno));
    351 		failure = -1;
    352 	}
    353 
    354 	for (;;) {
    355 		n_bytes += 1;
    356 		opcode = packet_get_char();
    357 		switch (opcode) {
    358 		case TTY_OP_END:
    359 			goto set;
    360 
    361 		/* XXX: future conflict possible */
    362 		case TTY_OP_ISPEED_PROTO1:
    363 		case TTY_OP_ISPEED_PROTO2:
    364 			n_bytes += 4;
    365 			baud = packet_get_int();
    366 			debug3("tty_parse_modes: ispeed %d", baud);
    367 			if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) == -1)
    368 				error("cfsetispeed failed for %d", baud);
    369 			break;
    370 
    371 		/* XXX: future conflict possible */
    372 		case TTY_OP_OSPEED_PROTO1:
    373 		case TTY_OP_OSPEED_PROTO2:
    374 			n_bytes += 4;
    375 			baud = packet_get_int();
    376 			debug3("tty_parse_modes: ospeed %d", baud);
    377 			if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) == -1)
    378 				error("cfsetospeed failed for %d", baud);
    379 			break;
    380 
    381 #define TTYCHAR(NAME, OP) \
    382 	case OP: \
    383 	  n_bytes += arg_size; \
    384 	  tio.c_cc[NAME] = get_arg(); \
    385 	  debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \
    386 	  break;
    387 #define TTYMODE(NAME, FIELD, OP) \
    388 	case OP: \
    389 	  n_bytes += arg_size; \
    390 	  if ((arg = get_arg())) \
    391 	    tio.FIELD |= NAME; \
    392 	  else \
    393 	    tio.FIELD &= ~NAME;	\
    394 	  debug3("tty_parse_modes: %d %d", OP, arg); \
    395 	  break;
    396 
    397 #include "ttymodes.h"
    398 
    399 #undef TTYCHAR
    400 #undef TTYMODE
    401 
    402 		default:
    403 			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
    404 			    opcode, opcode);
    405 			if (!compat20) {
    406 				/*
    407 				 * SSH1:
    408 				 * Opcodes 1 to 127 are defined to have
    409 				 * a one-byte argument.
    410 				 * Opcodes 128 to 159 are defined to have
    411 				 * an integer argument.
    412 				 */
    413 				if (opcode > 0 && opcode < 128) {
    414 					n_bytes += 1;
    415 					(void) packet_get_char();
    416 					break;
    417 				} else if (opcode >= 128 && opcode < 160) {
    418 					n_bytes += 4;
    419 					(void) packet_get_int();
    420 					break;
    421 				} else {
    422 					/*
    423 					 * It is a truly undefined opcode (160 to 255).
    424 					 * We have no idea about its arguments.  So we
    425 					 * must stop parsing.  Note that some data may be
    426 					 * left in the packet; hopefully there is nothing
    427 					 * more coming after the mode data.
    428 					 */
    429 					log("parse_tty_modes: unknown opcode %d", opcode);
    430 					goto set;
    431 				}
    432 			} else {
    433 				/*
    434 				 * SSH2:
    435 				 * Opcodes 1 to 159 are defined to have
    436 				 * a uint32 argument.
    437 				 * Opcodes 160 to 255 are undefined and
    438 				 * cause parsing to stop.
    439 				 */
    440 				if (opcode > 0 && opcode < 160) {
    441 					n_bytes += 4;
    442 					(void) packet_get_int();
    443 					break;
    444 				} else {
    445 					log("parse_tty_modes: unknown opcode %d", opcode);
    446 					goto set;
    447 				}
    448 			}
    449 		}
    450 	}
    451 
    452 set:
    453 	if (*n_bytes_ptr != n_bytes) {
    454 		*n_bytes_ptr = n_bytes;
    455 		log("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
    456 		    *n_bytes_ptr, n_bytes);
    457 		return;		/* Don't process bytes passed */
    458 	}
    459 	if (failure == -1)
    460 		return;		/* Packet parsed ok but tcgetattr() failed */
    461 
    462 	/* Set the new modes for the terminal. */
    463 	if (tcsetattr(fd, TCSANOW, &tio) == -1)
    464 		log("Setting tty modes failed: %.100s", strerror(errno));
    465 }
    466