ttymodes.c revision 181097
159138Smsmith/* $OpenBSD: ttymodes.c,v 1.26 2006/08/03 03:34:42 deraadt Exp $ */
259138Smsmith/*
359138Smsmith * Author: Tatu Ylonen <ylo@cs.hut.fi>
459138Smsmith * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
559138Smsmith *                    All rights reserved
659138Smsmith *
759138Smsmith * As far as I am concerned, the code I have written for this software
859138Smsmith * can be used freely for any purpose.  Any derived versions of this
959138Smsmith * software must be clearly marked as such, and if the derived work is
1059138Smsmith * incompatible with the protocol description in the RFC file, it must be
1159138Smsmith * called by a name other than "ssh" or "Secure Shell".
1259138Smsmith */
1359138Smsmith
1459138Smsmith/*
1559138Smsmith * SSH2 tty modes support by Kevin Steves.
1659138Smsmith * Copyright (c) 2001 Kevin Steves.  All rights reserved.
1759138Smsmith *
1859138Smsmith * Redistribution and use in source and binary forms, with or without
1959138Smsmith * modification, are permitted provided that the following conditions
2059138Smsmith * are met:
2159138Smsmith * 1. Redistributions of source code must retain the above copyright
2259138Smsmith *    notice, this list of conditions and the following disclaimer.
2359138Smsmith * 2. Redistributions in binary form must reproduce the above copyright
2459138Smsmith *    notice, this list of conditions and the following disclaimer in the
2559138Smsmith *    documentation and/or other materials provided with the distribution.
2659138Smsmith *
2759138Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2859138Smsmith * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2959138Smsmith * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
3059138Smsmith * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3159138Smsmith * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3259138Smsmith * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3359138Smsmith * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3459138Smsmith * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3559138Smsmith * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3659138Smsmith * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3759159Smsmith */
3859159Smsmith
3959138Smsmith/*
4059138Smsmith * Encoding and decoding of terminal modes in a portable way.
4159138Smsmith * Much of the format is defined in ttymodes.h; it is included multiple times
4259138Smsmith * into this file with the appropriate macro definitions to generate the
4359138Smsmith * suitable code.
4459138Smsmith */
4559138Smsmith
4659138Smsmith#include "includes.h"
4759138Smsmith
4859138Smsmith#include <sys/types.h>
4959138Smsmith
5059138Smsmith#include <errno.h>
5159138Smsmith#include <string.h>
5259138Smsmith#include <termios.h>
5359138Smsmith#include <stdarg.h>
5459138Smsmith
5559138Smsmith#include "packet.h"
5659138Smsmith#include "log.h"
5759138Smsmith#include "ssh1.h"
5859138Smsmith#include "compat.h"
5959138Smsmith#include "buffer.h"
6059138Smsmith
6159138Smsmith#define TTY_OP_END		0
6259138Smsmith/*
6359138Smsmith * uint32 (u_int) follows speed in SSH1 and SSH2
6459138Smsmith */
6559138Smsmith#define TTY_OP_ISPEED_PROTO1	192
6659138Smsmith#define TTY_OP_OSPEED_PROTO1	193
6759138Smsmith#define TTY_OP_ISPEED_PROTO2	128
6859138Smsmith#define TTY_OP_OSPEED_PROTO2	129
6959138Smsmith
7059138Smsmith/*
7159138Smsmith * Converts POSIX speed_t to a baud rate.  The values of the
7259138Smsmith * constants for speed_t are not themselves portable.
7359138Smsmith */
7459138Smsmithstatic int
7559138Smsmithspeed_to_baud(speed_t speed)
7659138Smsmith{
7759138Smsmith	switch (speed) {
7859138Smsmith	case B0:
7959138Smsmith		return 0;
8059138Smsmith	case B50:
8159138Smsmith		return 50;
8259138Smsmith	case B75:
8359138Smsmith		return 75;
8459138Smsmith	case B110:
8559138Smsmith		return 110;
8659138Smsmith	case B134:
8759138Smsmith		return 134;
8859138Smsmith	case B150:
8959138Smsmith		return 150;
9059138Smsmith	case B200:
9159138Smsmith		return 200;
9259138Smsmith	case B300:
9359138Smsmith		return 300;
9459138Smsmith	case B600:
9559138Smsmith		return 600;
9659138Smsmith	case B1200:
9759138Smsmith		return 1200;
9859138Smsmith	case B1800:
9959138Smsmith		return 1800;
10059138Smsmith	case B2400:
10159138Smsmith		return 2400;
10259138Smsmith	case B4800:
10359138Smsmith		return 4800;
10459138Smsmith	case B9600:
10559138Smsmith		return 9600;
10659138Smsmith
10759138Smsmith#ifdef B19200
10859138Smsmith	case B19200:
10959138Smsmith		return 19200;
11059138Smsmith#else /* B19200 */
11159138Smsmith#ifdef EXTA
11259138Smsmith	case EXTA:
11359138Smsmith		return 19200;
11459138Smsmith#endif /* EXTA */
11559138Smsmith#endif /* B19200 */
11659138Smsmith
11759138Smsmith#ifdef B38400
11859138Smsmith	case B38400:
11959138Smsmith		return 38400;
12059138Smsmith#else /* B38400 */
12159138Smsmith#ifdef EXTB
12259138Smsmith	case EXTB:
12359138Smsmith		return 38400;
12459138Smsmith#endif /* EXTB */
12559138Smsmith#endif /* B38400 */
12659138Smsmith
12759138Smsmith#ifdef B7200
12859138Smsmith	case B7200:
12959138Smsmith		return 7200;
13059138Smsmith#endif /* B7200 */
13159138Smsmith#ifdef B14400
13259138Smsmith	case B14400:
13359138Smsmith		return 14400;
13459138Smsmith#endif /* B14400 */
13559138Smsmith#ifdef B28800
13659138Smsmith	case B28800:
13759138Smsmith		return 28800;
13859138Smsmith#endif /* B28800 */
13959138Smsmith#ifdef B57600
14059138Smsmith	case B57600:
14159138Smsmith		return 57600;
14259138Smsmith#endif /* B57600 */
14359138Smsmith#ifdef B76800
14459138Smsmith	case B76800:
14559138Smsmith		return 76800;
14659138Smsmith#endif /* B76800 */
14759138Smsmith#ifdef B115200
14859138Smsmith	case B115200:
14959138Smsmith		return 115200;
15059138Smsmith#endif /* B115200 */
15159138Smsmith#ifdef B230400
15259138Smsmith	case B230400:
15359138Smsmith		return 230400;
15459138Smsmith#endif /* B230400 */
15559138Smsmith	default:
15659138Smsmith		return 9600;
15759138Smsmith	}
15859138Smsmith}
15959138Smsmith
16059138Smsmith/*
16159138Smsmith * Converts a numeric baud rate to a POSIX speed_t.
16259138Smsmith */
16359138Smsmithstatic speed_t
16459138Smsmithbaud_to_speed(int baud)
16559138Smsmith{
16659138Smsmith	switch (baud) {
16759138Smsmith	case 0:
16859138Smsmith		return B0;
16959138Smsmith	case 50:
17059138Smsmith		return B50;
17159138Smsmith	case 75:
17259138Smsmith		return B75;
17359138Smsmith	case 110:
17459138Smsmith		return B110;
17559138Smsmith	case 134:
17659138Smsmith		return B134;
17759138Smsmith	case 150:
17859138Smsmith		return B150;
17959138Smsmith	case 200:
18059138Smsmith		return B200;
18159138Smsmith	case 300:
18259138Smsmith		return B300;
18359138Smsmith	case 600:
18459138Smsmith		return B600;
18559138Smsmith	case 1200:
18659138Smsmith		return B1200;
18759138Smsmith	case 1800:
18859138Smsmith		return B1800;
18959138Smsmith	case 2400:
19059138Smsmith		return B2400;
19159138Smsmith	case 4800:
19259138Smsmith		return B4800;
19359138Smsmith	case 9600:
19459138Smsmith		return B9600;
19559138Smsmith
19659138Smsmith#ifdef B19200
19759138Smsmith	case 19200:
19859138Smsmith		return B19200;
19959138Smsmith#else /* B19200 */
20059138Smsmith#ifdef EXTA
20159138Smsmith	case 19200:
20259138Smsmith		return EXTA;
20359138Smsmith#endif /* EXTA */
20459138Smsmith#endif /* B19200 */
20559138Smsmith
20659138Smsmith#ifdef B38400
20759138Smsmith	case 38400:
20859138Smsmith		return B38400;
20959138Smsmith#else /* B38400 */
21059138Smsmith#ifdef EXTB
21159138Smsmith	case 38400:
21259138Smsmith		return EXTB;
21359138Smsmith#endif /* EXTB */
21459138Smsmith#endif /* B38400 */
21559138Smsmith
21659138Smsmith#ifdef B7200
21759138Smsmith	case 7200:
21859138Smsmith		return B7200;
21959138Smsmith#endif /* B7200 */
22059138Smsmith#ifdef B14400
22159138Smsmith	case 14400:
22259138Smsmith		return B14400;
22359138Smsmith#endif /* B14400 */
22459138Smsmith#ifdef B28800
22559138Smsmith	case 28800:
22659138Smsmith		return B28800;
22759138Smsmith#endif /* B28800 */
22859138Smsmith#ifdef B57600
22959138Smsmith	case 57600:
23059138Smsmith		return B57600;
23159138Smsmith#endif /* B57600 */
23259138Smsmith#ifdef B76800
23359138Smsmith	case 76800:
23459138Smsmith		return B76800;
23559138Smsmith#endif /* B76800 */
23659138Smsmith#ifdef B115200
23759138Smsmith	case 115200:
23859138Smsmith		return B115200;
23959138Smsmith#endif /* B115200 */
24059138Smsmith#ifdef B230400
24159138Smsmith	case 230400:
24259138Smsmith		return B230400;
24359138Smsmith#endif /* B230400 */
24459138Smsmith	default:
24559138Smsmith		return B9600;
24659138Smsmith	}
24759138Smsmith}
24859138Smsmith
24959138Smsmith/*
25059138Smsmith * Encode a special character into SSH line format.
25159138Smsmith */
25259138Smsmithstatic u_int
25359138Smsmithspecial_char_encode(cc_t c)
25459138Smsmith{
25559138Smsmith#ifdef _POSIX_VDISABLE
25659138Smsmith	if (c == _POSIX_VDISABLE)
25759138Smsmith		return 255;
25859138Smsmith#endif /* _POSIX_VDISABLE */
25959138Smsmith	return c;
26059138Smsmith}
26159138Smsmith
26259138Smsmith/*
26359138Smsmith * Decode a special character from SSH line format.
26459138Smsmith */
26559138Smsmithstatic cc_t
26659138Smsmithspecial_char_decode(u_int c)
26759138Smsmith{
26859138Smsmith#ifdef _POSIX_VDISABLE
26959138Smsmith	if (c == 255)
27059138Smsmith		return _POSIX_VDISABLE;
27159138Smsmith#endif /* _POSIX_VDISABLE */
27259138Smsmith	return c;
27359138Smsmith}
27459138Smsmith
27559138Smsmith/*
27659138Smsmith * Encodes terminal modes for the terminal referenced by fd
27759138Smsmith * or tiop in a portable manner, and appends the modes to a packet
27859138Smsmith * being constructed.
27959138Smsmith */
28059138Smsmithvoid
28159138Smsmithtty_make_modes(int fd, struct termios *tiop)
28259138Smsmith{
28359138Smsmith	struct termios tio;
28459138Smsmith	int baud;
28559138Smsmith	Buffer buf;
28659138Smsmith	int tty_op_ospeed, tty_op_ispeed;
28759138Smsmith	void (*put_arg)(Buffer *, u_int);
28859138Smsmith
28959138Smsmith	buffer_init(&buf);
29059138Smsmith	if (compat20) {
29159138Smsmith		tty_op_ospeed = TTY_OP_OSPEED_PROTO2;
29259138Smsmith		tty_op_ispeed = TTY_OP_ISPEED_PROTO2;
29359138Smsmith		put_arg = buffer_put_int;
29459138Smsmith	} else {
29559138Smsmith		tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
29659138Smsmith		tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
29759138Smsmith		put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
29859138Smsmith	}
29959138Smsmith
30059138Smsmith	if (tiop == NULL) {
30159138Smsmith		if (tcgetattr(fd, &tio) == -1) {
30259138Smsmith			logit("tcgetattr: %.100s", strerror(errno));
30359138Smsmith			goto end;
30459138Smsmith		}
30559138Smsmith	} else
30659138Smsmith		tio = *tiop;
30759138Smsmith
30859138Smsmith	/* Store input and output baud rates. */
30959138Smsmith	baud = speed_to_baud(cfgetospeed(&tio));
31059138Smsmith	debug3("tty_make_modes: ospeed %d", baud);
31159138Smsmith	buffer_put_char(&buf, tty_op_ospeed);
31259138Smsmith	buffer_put_int(&buf, baud);
31359138Smsmith	baud = speed_to_baud(cfgetispeed(&tio));
31459138Smsmith	debug3("tty_make_modes: ispeed %d", baud);
31559138Smsmith	buffer_put_char(&buf, tty_op_ispeed);
31659138Smsmith	buffer_put_int(&buf, baud);
31759138Smsmith
31859138Smsmith	/* Store values of mode flags. */
31959138Smsmith#define TTYCHAR(NAME, OP) \
32059138Smsmith	debug3("tty_make_modes: %d %d", OP, tio.c_cc[NAME]); \
32159138Smsmith	buffer_put_char(&buf, OP); \
32259138Smsmith	put_arg(&buf, special_char_encode(tio.c_cc[NAME]));
32359138Smsmith
32459138Smsmith#define TTYMODE(NAME, FIELD, OP) \
32559138Smsmith	debug3("tty_make_modes: %d %d", OP, ((tio.FIELD & NAME) != 0)); \
32659138Smsmith	buffer_put_char(&buf, OP); \
32759138Smsmith	put_arg(&buf, ((tio.FIELD & NAME) != 0));
32859138Smsmith
32959138Smsmith#include "ttymodes.h"
33059138Smsmith
33159138Smsmith#undef TTYCHAR
33259138Smsmith#undef TTYMODE
33359138Smsmith
33459138Smsmithend:
33559138Smsmith	/* Mark end of mode data. */
33659138Smsmith	buffer_put_char(&buf, TTY_OP_END);
33759138Smsmith	if (compat20)
33859138Smsmith		packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
33959138Smsmith	else
34059138Smsmith		packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
34159138Smsmith	buffer_free(&buf);
34259138Smsmith}
34359138Smsmith
34459138Smsmith/*
34559138Smsmith * Decodes terminal modes for the terminal referenced by fd in a portable
34659138Smsmith * manner from a packet being read.
34759138Smsmith */
34859138Smsmithvoid
34959138Smsmithtty_parse_modes(int fd, int *n_bytes_ptr)
35059138Smsmith{
35159138Smsmith	struct termios tio;
35259138Smsmith	int opcode, baud;
35359138Smsmith	int n_bytes = 0;
35459138Smsmith	int failure = 0;
35559138Smsmith	u_int (*get_arg)(void);
35659138Smsmith	int arg, arg_size;
35759138Smsmith
35859138Smsmith	if (compat20) {
35959138Smsmith		*n_bytes_ptr = packet_get_int();
36059138Smsmith		debug3("tty_parse_modes: SSH2 n_bytes %d", *n_bytes_ptr);
36159138Smsmith		if (*n_bytes_ptr == 0)
36259138Smsmith			return;
36359138Smsmith		get_arg = packet_get_int;
36459138Smsmith		arg_size = 4;
36559138Smsmith	} else {
36659138Smsmith		get_arg = packet_get_char;
36759138Smsmith		arg_size = 1;
36859138Smsmith	}
36959138Smsmith
37059138Smsmith	/*
37159138Smsmith	 * Get old attributes for the terminal.  We will modify these
37259138Smsmith	 * flags. I am hoping that if there are any machine-specific
37359138Smsmith	 * modes, they will initially have reasonable values.
37459138Smsmith	 */
37559138Smsmith	if (tcgetattr(fd, &tio) == -1) {
37659138Smsmith		logit("tcgetattr: %.100s", strerror(errno));
37759138Smsmith		failure = -1;
37859138Smsmith	}
37959138Smsmith
38059138Smsmith	for (;;) {
38159138Smsmith		n_bytes += 1;
38259138Smsmith		opcode = packet_get_char();
38359138Smsmith		switch (opcode) {
38459138Smsmith		case TTY_OP_END:
38559138Smsmith			goto set;
38659138Smsmith
38759138Smsmith		/* XXX: future conflict possible */
38859138Smsmith		case TTY_OP_ISPEED_PROTO1:
38959138Smsmith		case TTY_OP_ISPEED_PROTO2:
39059138Smsmith			n_bytes += 4;
39159138Smsmith			baud = packet_get_int();
39259138Smsmith			debug3("tty_parse_modes: ispeed %d", baud);
39359138Smsmith			if (failure != -1 &&
39459138Smsmith			    cfsetispeed(&tio, baud_to_speed(baud)) == -1)
39559138Smsmith				error("cfsetispeed failed for %d", baud);
39659138Smsmith			break;
39759138Smsmith
39859138Smsmith		/* XXX: future conflict possible */
39959138Smsmith		case TTY_OP_OSPEED_PROTO1:
40059138Smsmith		case TTY_OP_OSPEED_PROTO2:
40159138Smsmith			n_bytes += 4;
40259138Smsmith			baud = packet_get_int();
40359138Smsmith			debug3("tty_parse_modes: ospeed %d", baud);
40459138Smsmith			if (failure != -1 &&
40559138Smsmith			    cfsetospeed(&tio, baud_to_speed(baud)) == -1)
40659138Smsmith				error("cfsetospeed failed for %d", baud);
40759138Smsmith			break;
40859138Smsmith
40959138Smsmith#define TTYCHAR(NAME, OP) \
41059138Smsmith	case OP: \
41159138Smsmith	  n_bytes += arg_size; \
41259138Smsmith	  tio.c_cc[NAME] = special_char_decode(get_arg()); \
41359138Smsmith	  debug3("tty_parse_modes: %d %d", OP, tio.c_cc[NAME]); \
41459138Smsmith	  break;
41559138Smsmith#define TTYMODE(NAME, FIELD, OP) \
41659138Smsmith	case OP: \
41759138Smsmith	  n_bytes += arg_size; \
41859138Smsmith	  if ((arg = get_arg())) \
41959138Smsmith	    tio.FIELD |= NAME; \
42059138Smsmith	  else \
42159138Smsmith	    tio.FIELD &= ~NAME;	\
42259138Smsmith	  debug3("tty_parse_modes: %d %d", OP, arg); \
42359138Smsmith	  break;
42459138Smsmith
42559138Smsmith#include "ttymodes.h"
42659138Smsmith
42759138Smsmith#undef TTYCHAR
42859138Smsmith#undef TTYMODE
42959138Smsmith
43059138Smsmith		default:
43159138Smsmith			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
43259138Smsmith			    opcode, opcode);
43359138Smsmith			if (!compat20) {
43459138Smsmith				/*
43559138Smsmith				 * SSH1:
43659138Smsmith				 * Opcodes 1 to 127 are defined to have
43759138Smsmith				 * a one-byte argument.
43859138Smsmith				 * Opcodes 128 to 159 are defined to have
43959138Smsmith				 * an integer argument.
44059138Smsmith				 */
44159138Smsmith				if (opcode > 0 && opcode < 128) {
44259138Smsmith					n_bytes += 1;
44359138Smsmith					(void) packet_get_char();
44459138Smsmith					break;
44559138Smsmith				} else if (opcode >= 128 && opcode < 160) {
44659138Smsmith					n_bytes += 4;
44759138Smsmith					(void) packet_get_int();
44859138Smsmith					break;
44959138Smsmith				} else {
45059138Smsmith					/*
45159138Smsmith					 * It is a truly undefined opcode (160 to 255).
45259138Smsmith					 * We have no idea about its arguments.  So we
45359138Smsmith					 * must stop parsing.  Note that some data
45459138Smsmith					 * may be left in the packet; hopefully there
45559138Smsmith					 * is nothing more coming after the mode data.
45659138Smsmith					 */
45759138Smsmith					logit("parse_tty_modes: unknown opcode %d",
45859138Smsmith					    opcode);
45959138Smsmith					goto set;
46059138Smsmith				}
46159138Smsmith			} else {
46259138Smsmith				/*
46359138Smsmith				 * SSH2:
46459138Smsmith				 * Opcodes 1 to 159 are defined to have
46559138Smsmith				 * a uint32 argument.
46659138Smsmith				 * Opcodes 160 to 255 are undefined and
46759138Smsmith				 * cause parsing to stop.
46859138Smsmith				 */
46959138Smsmith				if (opcode > 0 && opcode < 160) {
47059138Smsmith					n_bytes += 4;
47159138Smsmith					(void) packet_get_int();
47259138Smsmith					break;
47359138Smsmith				} else {
47459138Smsmith					logit("parse_tty_modes: unknown opcode %d",
47559138Smsmith					    opcode);
47659138Smsmith					goto set;
47759138Smsmith				}
47859138Smsmith			}
47959138Smsmith		}
48059138Smsmith	}
48159138Smsmith
48259138Smsmithset:
48359138Smsmith	if (*n_bytes_ptr != n_bytes) {
48459138Smsmith		*n_bytes_ptr = n_bytes;
48559138Smsmith		logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
48659138Smsmith		    *n_bytes_ptr, n_bytes);
48759138Smsmith		return;		/* Don't process bytes passed */
48859138Smsmith	}
48959138Smsmith	if (failure == -1)
49059138Smsmith		return;		/* Packet parsed ok but tcgetattr() failed */
49159138Smsmith
49259138Smsmith	/* Set the new modes for the terminal. */
49359138Smsmith	if (tcsetattr(fd, TCSANOW, &tio) == -1)
49459138Smsmith		logit("Setting tty modes failed: %.100s", strerror(errno));
49559138Smsmith}
49659138Smsmith