ttymodes.c revision 162853
11541Srgrimes/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */
21541Srgrimes/*
31541Srgrimes * Author: Tatu Ylonen <ylo@cs.hut.fi>
41541Srgrimes * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
51541Srgrimes *                    All rights reserved
61541Srgrimes *
71541Srgrimes * As far as I am concerned, the code I have written for this software
81541Srgrimes * can be used freely for any purpose.  Any derived versions of this
91541Srgrimes * software must be clearly marked as such, and if the derived work is
101541Srgrimes * incompatible with the protocol description in the RFC file, it must be
111541Srgrimes * called by a name other than "ssh" or "Secure Shell".
121541Srgrimes */
131541Srgrimes
141541Srgrimes/*
151541Srgrimes * SSH2 tty modes support by Kevin Steves.
161541Srgrimes * Copyright (c) 2001 Kevin Steves.  All rights reserved.
171541Srgrimes *
181541Srgrimes * Redistribution and use in source and binary forms, with or without
191541Srgrimes * modification, are permitted provided that the following conditions
201541Srgrimes * are met:
211541Srgrimes * 1. Redistributions of source code must retain the above copyright
221541Srgrimes *    notice, this list of conditions and the following disclaimer.
231541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
241541Srgrimes *    notice, this list of conditions and the following disclaimer in the
251541Srgrimes *    documentation and/or other materials provided with the distribution.
261541Srgrimes *
271541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
281541Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
291541Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
301541Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
311541Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
321541Srgrimes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
331541Srgrimes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
341541Srgrimes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
351541Srgrimes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
361817Sdg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
371541Srgrimes */
381541Srgrimes
391541Srgrimes/*
401541Srgrimes * Encoding and decoding of terminal modes in a portable way.
411541Srgrimes * Much of the format is defined in ttymodes.h; it is included multiple times
421541Srgrimes * into this file with the appropriate macro definitions to generate the
431541Srgrimes * suitable code.
441541Srgrimes */
451541Srgrimes
461541Srgrimes#include "includes.h"
471541Srgrimes
481541Srgrimes#include <sys/types.h>
491541Srgrimes
501541Srgrimes#include <errno.h>
511541Srgrimes#include <string.h>
521541Srgrimes#include <termios.h>
531541Srgrimes#include <stdarg.h>
541541Srgrimes
551541Srgrimes#include "packet.h"
561541Srgrimes#include "log.h"
571541Srgrimes#include "ssh1.h"
581541Srgrimes#include "compat.h"
591541Srgrimes#include "buffer.h"
601541Srgrimes
611541Srgrimes#define TTY_OP_END		0
621541Srgrimes/*
631817Sdg * uint32 (u_int) follows speed in SSH1 and SSH2
641817Sdg */
651541Srgrimes#define TTY_OP_ISPEED_PROTO1	192
661541Srgrimes#define TTY_OP_OSPEED_PROTO1	193
671541Srgrimes#define TTY_OP_ISPEED_PROTO2	128
681541Srgrimes#define TTY_OP_OSPEED_PROTO2	129
691541Srgrimes
701541Srgrimes/*
711541Srgrimes * Converts POSIX speed_t to a baud rate.  The values of the
721541Srgrimes * constants for speed_t are not themselves portable.
731541Srgrimes */
741541Srgrimesstatic int
751541Srgrimesspeed_to_baud(speed_t speed)
761541Srgrimes{
771541Srgrimes	switch (speed) {
781541Srgrimes	case B0:
791541Srgrimes		return 0;
801541Srgrimes	case B50:
811541Srgrimes		return 50;
821541Srgrimes	case B75:
831541Srgrimes		return 75;
841541Srgrimes	case B110:
851541Srgrimes		return 110;
861541Srgrimes	case B134:
871541Srgrimes		return 134;
881541Srgrimes	case B150:
891541Srgrimes		return 150;
901541Srgrimes	case B200:
911541Srgrimes		return 200;
921541Srgrimes	case B300:
931541Srgrimes		return 300;
941541Srgrimes	case B600:
951541Srgrimes		return 600;
961541Srgrimes	case B1200:
971541Srgrimes		return 1200;
981541Srgrimes	case B1800:
991541Srgrimes		return 1800;
1001541Srgrimes	case B2400:
1011541Srgrimes		return 2400;
1021541Srgrimes	case B4800:
1031541Srgrimes		return 4800;
1041541Srgrimes	case B9600:
1051541Srgrimes		return 9600;
1061541Srgrimes
1071541Srgrimes#ifdef B19200
1081541Srgrimes	case B19200:
1091541Srgrimes		return 19200;
1101541Srgrimes#else /* B19200 */
1111541Srgrimes#ifdef EXTA
1121541Srgrimes	case EXTA:
1131541Srgrimes		return 19200;
1141541Srgrimes#endif /* EXTA */
1151541Srgrimes#endif /* B19200 */
1161541Srgrimes
1171541Srgrimes#ifdef B38400
1181541Srgrimes	case B38400:
1191541Srgrimes		return 38400;
1201541Srgrimes#else /* B38400 */
1211541Srgrimes#ifdef EXTB
1221541Srgrimes	case EXTB:
1231541Srgrimes		return 38400;
1241541Srgrimes#endif /* EXTB */
1251541Srgrimes#endif /* B38400 */
1261541Srgrimes
1271541Srgrimes#ifdef B7200
1281541Srgrimes	case B7200:
1291541Srgrimes		return 7200;
1301541Srgrimes#endif /* B7200 */
1311541Srgrimes#ifdef B14400
1321541Srgrimes	case B14400:
1331541Srgrimes		return 14400;
1341541Srgrimes#endif /* B14400 */
1351541Srgrimes#ifdef B28800
1361541Srgrimes	case B28800:
1371541Srgrimes		return 28800;
1381541Srgrimes#endif /* B28800 */
1391541Srgrimes#ifdef B57600
1401541Srgrimes	case B57600:
1411541Srgrimes		return 57600;
1421541Srgrimes#endif /* B57600 */
1431541Srgrimes#ifdef B76800
1441541Srgrimes	case B76800:
1451541Srgrimes		return 76800;
1461541Srgrimes#endif /* B76800 */
1471541Srgrimes#ifdef B115200
1481541Srgrimes	case B115200:
1491541Srgrimes		return 115200;
1501541Srgrimes#endif /* B115200 */
1511541Srgrimes#ifdef B230400
1521541Srgrimes	case B230400:
1531541Srgrimes		return 230400;
1541541Srgrimes#endif /* B230400 */
1551541Srgrimes	default:
1561541Srgrimes		return 9600;
1571541Srgrimes	}
1581541Srgrimes}
1591541Srgrimes
1601541Srgrimes/*
1611541Srgrimes * Converts a numeric baud rate to a POSIX speed_t.
1621541Srgrimes */
1631541Srgrimesstatic speed_t
1641541Srgrimesbaud_to_speed(int baud)
1651541Srgrimes{
1661541Srgrimes	switch (baud) {
1671541Srgrimes	case 0:
1681541Srgrimes		return B0;
1691541Srgrimes	case 50:
1701541Srgrimes		return B50;
1711541Srgrimes	case 75:
1721541Srgrimes		return B75;
1731541Srgrimes	case 110:
1741541Srgrimes		return B110;
1751541Srgrimes	case 134:
1761541Srgrimes		return B134;
1771541Srgrimes	case 150:
1781541Srgrimes		return B150;
1791541Srgrimes	case 200:
1801541Srgrimes		return B200;
1811541Srgrimes	case 300:
1821541Srgrimes		return B300;
1831541Srgrimes	case 600:
1841541Srgrimes		return B600;
1851541Srgrimes	case 1200:
1861541Srgrimes		return B1200;
1871541Srgrimes	case 1800:
1881541Srgrimes		return B1800;
1891541Srgrimes	case 2400:
1901541Srgrimes		return B2400;
1911541Srgrimes	case 4800:
1921541Srgrimes		return B4800;
1931541Srgrimes	case 9600:
1941541Srgrimes		return B9600;
1951541Srgrimes
1961541Srgrimes#ifdef B19200
1971541Srgrimes	case 19200:
1981541Srgrimes		return B19200;
1991541Srgrimes#else /* B19200 */
2001541Srgrimes#ifdef EXTA
2011541Srgrimes	case 19200:
2021541Srgrimes		return EXTA;
2031541Srgrimes#endif /* EXTA */
2041541Srgrimes#endif /* B19200 */
2051541Srgrimes
2061541Srgrimes#ifdef B38400
2071541Srgrimes	case 38400:
2081541Srgrimes		return B38400;
2091541Srgrimes#else /* B38400 */
2101541Srgrimes#ifdef EXTB
2111541Srgrimes	case 38400:
2121541Srgrimes		return EXTB;
2131541Srgrimes#endif /* EXTB */
2141541Srgrimes#endif /* B38400 */
2151541Srgrimes
2161541Srgrimes#ifdef B7200
2171541Srgrimes	case 7200:
2181541Srgrimes		return B7200;
2191541Srgrimes#endif /* B7200 */
2201541Srgrimes#ifdef B14400
2211541Srgrimes	case 14400:
2221541Srgrimes		return B14400;
2231541Srgrimes#endif /* B14400 */
2241541Srgrimes#ifdef B28800
2251541Srgrimes	case 28800:
2261541Srgrimes		return B28800;
2271541Srgrimes#endif /* B28800 */
2281541Srgrimes#ifdef B57600
2291541Srgrimes	case 57600:
2301541Srgrimes		return B57600;
2311541Srgrimes#endif /* B57600 */
2321541Srgrimes#ifdef B76800
2331541Srgrimes	case 76800:
2341541Srgrimes		return B76800;
2351541Srgrimes#endif /* B76800 */
2361541Srgrimes#ifdef B115200
2371541Srgrimes	case 115200:
2381541Srgrimes		return B115200;
2391541Srgrimes#endif /* B115200 */
2401541Srgrimes#ifdef B230400
2411541Srgrimes	case 230400:
2421541Srgrimes		return B230400;
2431541Srgrimes#endif /* B230400 */
2441541Srgrimes	default:
2451541Srgrimes		return B9600;
2461541Srgrimes	}
2471541Srgrimes}
2481541Srgrimes
2491541Srgrimes/*
2501541Srgrimes * Encode a special character into SSH line format.
2511541Srgrimes */
2521541Srgrimesstatic u_int
2531541Srgrimesspecial_char_encode(cc_t c)
2541541Srgrimes{
2551541Srgrimes#ifdef _POSIX_VDISABLE
2561541Srgrimes	if (c == _POSIX_VDISABLE)
2571541Srgrimes		return 255;
2581541Srgrimes#endif /* _POSIX_VDISABLE */
2591541Srgrimes	return c;
2601541Srgrimes}
2611541Srgrimes
2621541Srgrimes/*
2631541Srgrimes * Decode a special character from SSH line format.
2641541Srgrimes */
2651541Srgrimesstatic cc_t
2661541Srgrimesspecial_char_decode(u_int c)
2671541Srgrimes{
2681541Srgrimes#ifdef _POSIX_VDISABLE
2691541Srgrimes	if (c == 255)
2701541Srgrimes		return _POSIX_VDISABLE;
2711541Srgrimes#endif /* _POSIX_VDISABLE */
2721541Srgrimes	return c;
2731541Srgrimes}
2741541Srgrimes
2751541Srgrimes/*
2761541Srgrimes * Encodes terminal modes for the terminal referenced by fd
2771541Srgrimes * or tiop in a portable manner, and appends the modes to a packet
2781541Srgrimes * being constructed.
2791541Srgrimes */
2801541Srgrimesvoid
2811541Srgrimestty_make_modes(int fd, struct termios *tiop)
2821541Srgrimes{
2831541Srgrimes	struct termios tio;
2841541Srgrimes	int baud;
2851541Srgrimes	Buffer buf;
2861541Srgrimes	int tty_op_ospeed, tty_op_ispeed;
2871541Srgrimes	void (*put_arg)(Buffer *, u_int);
2881541Srgrimes
2891541Srgrimes	buffer_init(&buf);
2901541Srgrimes	if (compat20) {
2911541Srgrimes		tty_op_ospeed = TTY_OP_OSPEED_PROTO2;
2921541Srgrimes		tty_op_ispeed = TTY_OP_ISPEED_PROTO2;
2931541Srgrimes		put_arg = buffer_put_int;
2941541Srgrimes	} else {
2951541Srgrimes		tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
2961541Srgrimes		tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
2971549Srgrimes		put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
2981541Srgrimes	}
2991541Srgrimes
3001541Srgrimes	if (tiop == NULL) {
3011549Srgrimes		if (tcgetattr(fd, &tio) == -1) {
3021549Srgrimes			logit("tcgetattr: %.100s", strerror(errno));
3031549Srgrimes			goto end;
3041541Srgrimes		}
3051541Srgrimes	} else
3061541Srgrimes		tio = *tiop;
3071541Srgrimes
3081541Srgrimes	/* Store input and output baud rates. */
3091541Srgrimes	baud = speed_to_baud(cfgetospeed(&tio));
3101541Srgrimes	debug3("tty_make_modes: ospeed %d", baud);
3111541Srgrimes	buffer_put_char(&buf, tty_op_ospeed);
3121541Srgrimes	buffer_put_int(&buf, baud);
3131541Srgrimes	baud = speed_to_baud(cfgetispeed(&tio));
3141541Srgrimes	debug3("tty_make_modes: ispeed %d", baud);
3151541Srgrimes	buffer_put_char(&buf, tty_op_ispeed);
3161541Srgrimes	buffer_put_int(&buf, baud);
3171541Srgrimes
3181541Srgrimes	/* Store values of mode flags. */
3191541Srgrimes#define TTYCHAR(NAME, OP) \
3201541Srgrimes	debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \
3211541Srgrimes	buffer_put_char(&buf, OP); \
3221541Srgrimes	put_arg(&buf, special_char_encode(tio.c_cc[NAME]));
3231541Srgrimes
3241541Srgrimes#define TTYMODE(NAME, FIELD, OP) \
3251541Srgrimes	debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \
3261541Srgrimes	buffer_put_char(&buf, OP); \
3271541Srgrimes	put_arg(&buf, ((tio.FIELD & NAME) != 0));
3281541Srgrimes
3291541Srgrimes#include "ttymodes.h"
3301541Srgrimes
3311541Srgrimes#undef TTYCHAR
3321541Srgrimes#undef TTYMODE
3331541Srgrimes
3341541Srgrimesend:
3351541Srgrimes	/* Mark end of mode data. */
3361541Srgrimes	buffer_put_char(&buf, TTY_OP_END);
3371541Srgrimes	if (compat20)
3381541Srgrimes		packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
3391541Srgrimes	else
3401541Srgrimes		packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
3411541Srgrimes	buffer_free(&buf);
3421541Srgrimes}
3431541Srgrimes
3441541Srgrimes/*
3451541Srgrimes * Decodes terminal modes for the terminal referenced by fd in a portable
3461541Srgrimes * manner from a packet being read.
3471541Srgrimes */
3481541Srgrimesvoid
3491541Srgrimestty_parse_modes(int fd, int *n_bytes_ptr)
3501541Srgrimes{
3511541Srgrimes	struct termios tio;
3521541Srgrimes	int opcode, baud;
3531541Srgrimes	int n_bytes = 0;
3541541Srgrimes	int failure = 0;
3551541Srgrimes	u_int (*get_arg)(void);
3561541Srgrimes	int arg, arg_size;
3571541Srgrimes
3581541Srgrimes	if (compat20) {
3591541Srgrimes		*n_bytes_ptr = packet_get_int();
3601541Srgrimes		debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr);
3611541Srgrimes		if (*n_bytes_ptr == 0)
3621541Srgrimes			return;
3631541Srgrimes		get_arg = packet_get_int;
3641541Srgrimes		arg_size = 4;
3651541Srgrimes	} else {
3661541Srgrimes		get_arg = packet_get_char;
3671541Srgrimes		arg_size = 1;
3681541Srgrimes	}
3691541Srgrimes
3701541Srgrimes	/*
3711541Srgrimes	 * Get old attributes for the terminal.  We will modify these
3721541Srgrimes	 * flags. I am hoping that if there are any machine-specific
3731541Srgrimes	 * modes, they will initially have reasonable values.
3741541Srgrimes	 */
3751541Srgrimes	if (tcgetattr(fd, &tio) == -1) {
3761541Srgrimes		logit("tcgetattr: %.100s", strerror(errno));
3771541Srgrimes		failure = -1;
3781541Srgrimes	}
3791541Srgrimes
3801541Srgrimes	for (;;) {
3811541Srgrimes		n_bytes += 1;
3821541Srgrimes		opcode = packet_get_char();
3831541Srgrimes		switch (opcode) {
3841541Srgrimes		case TTY_OP_END:
3851541Srgrimes			goto set;
3861541Srgrimes
3871541Srgrimes		/* XXX: future conflict possible */
3881541Srgrimes		case TTY_OP_ISPEED_PROTO1:
3891541Srgrimes		case TTY_OP_ISPEED_PROTO2:
3901541Srgrimes			n_bytes += 4;
3911541Srgrimes			baud = packet_get_int();
3921541Srgrimes			debug3("tty_parse_modes: ispeed %d", baud);
3931541Srgrimes			if (failure != -1 &&
3941541Srgrimes			    cfsetispeed(&tio, baud_to_speed(baud)) == -1)
3951541Srgrimes				error("cfsetispeed failed for %d", baud);
3961541Srgrimes			break;
3971541Srgrimes
3981541Srgrimes		/* XXX: future conflict possible */
3991541Srgrimes		case TTY_OP_OSPEED_PROTO1:
4001541Srgrimes		case TTY_OP_OSPEED_PROTO2:
4011541Srgrimes			n_bytes += 4;
4021541Srgrimes			baud = packet_get_int();
4031541Srgrimes			debug3("tty_parse_modes: ospeed %d", baud);
4041541Srgrimes			if (failure != -1 &&
4051541Srgrimes			    cfsetospeed(&tio, baud_to_speed(baud)) == -1)
4061541Srgrimes				error("cfsetospeed failed for %d", baud);
4071541Srgrimes			break;
4081541Srgrimes
4091541Srgrimes#define TTYCHAR(NAME, OP) \
4101541Srgrimes	case OP: \
4111541Srgrimes	  n_bytes += arg_size; \
4121541Srgrimes	  tio.c_cc[NAME] = special_char_decode(get_arg()); \
4131549Srgrimes	  debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \
4141541Srgrimes	  break;
4151541Srgrimes#define TTYMODE(NAME, FIELD, OP) \
4161541Srgrimes	case OP: \
4171541Srgrimes	  n_bytes += arg_size; \
4181541Srgrimes	  if ((arg = get_arg())) \
4191541Srgrimes	    tio.FIELD |= NAME; \
4201541Srgrimes	  else \
4211541Srgrimes	    tio.FIELD &= ~NAME;	\
4221541Srgrimes	  debug3("tty_parse_modes: %d %d", OP, arg); \
4231541Srgrimes	  break;
4241541Srgrimes
4251541Srgrimes#include "ttymodes.h"
4261541Srgrimes
4271541Srgrimes#undef TTYCHAR
4281541Srgrimes#undef TTYMODE
4291541Srgrimes
4301541Srgrimes		default:
4311541Srgrimes			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
4321541Srgrimes			    opcode, opcode);
4331541Srgrimes			if (!compat20) {
4341541Srgrimes				/*
4351541Srgrimes				 * SSH1:
4361541Srgrimes				 * Opcodes 1 to 127 are defined to have
4371541Srgrimes				 * a one-byte argument.
4381541Srgrimes				 * Opcodes 128 to 159 are defined to have
4391541Srgrimes				 * an integer argument.
4401541Srgrimes				 */
4411541Srgrimes				if (opcode > 0 && opcode < 128) {
4421541Srgrimes					n_bytes += 1;
4431541Srgrimes					(void) packet_get_char();
4441541Srgrimes					break;
4451541Srgrimes				} else if (opcode >= 128 && opcode < 160) {
4461541Srgrimes					n_bytes += 4;
4471541Srgrimes					(void) packet_get_int();
4481541Srgrimes					break;
4491541Srgrimes				} else {
4501541Srgrimes					/*
4511541Srgrimes					 * It is a truly undefined opcode (160 to 255).
4521541Srgrimes					 * We have no idea about its arguments.  So we
4531541Srgrimes					 * must stop parsing.  Note that some data
4541541Srgrimes					 * may be left in the packet; hopefully there
4551541Srgrimes					 * is nothing more coming after the mode data.
4561541Srgrimes					 */
457					logit("parse_tty_modes: unknown opcode %d",
458					    opcode);
459					goto set;
460				}
461			} else {
462				/*
463				 * SSH2:
464				 * Opcodes 1 to 159 are defined to have
465				 * a uint32 argument.
466				 * Opcodes 160 to 255 are undefined and
467				 * cause parsing to stop.
468				 */
469				if (opcode > 0 && opcode < 160) {
470					n_bytes += 4;
471					(void) packet_get_int();
472					break;
473				} else {
474					logit("parse_tty_modes: unknown opcode %d",
475					    opcode);
476					goto set;
477				}
478			}
479		}
480	}
481
482set:
483	if (*n_bytes_ptr != n_bytes) {
484		*n_bytes_ptr = n_bytes;
485		logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
486		    *n_bytes_ptr, n_bytes);
487		return;		/* Don't process bytes passed */
488	}
489	if (failure == -1)
490		return;		/* Packet parsed ok but tcgetattr() failed */
491
492	/* Set the new modes for the terminal. */
493	if (tcsetattr(fd, TCSANOW, &tio) == -1)
494		logit("Setting tty modes failed: %.100s", strerror(errno));
495}
496