1323124Sdes/* $OpenBSD: ttymodes.c,v 1.30 2016/05/04 14:22:33 markus Exp $ */
257429Smarkm/*
357429Smarkm * Author: Tatu Ylonen <ylo@cs.hut.fi>
457429Smarkm * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
557429Smarkm *                    All rights reserved
665668Skris *
765668Skris * As far as I am concerned, the code I have written for this software
865668Skris * can be used freely for any purpose.  Any derived versions of this
965668Skris * software must be clearly marked as such, and if the derived work is
1065668Skris * incompatible with the protocol description in the RFC file, it must be
1165668Skris * called by a name other than "ssh" or "Secure Shell".
1257429Smarkm */
1357429Smarkm
1476259Sgreen/*
1576259Sgreen * SSH2 tty modes support by Kevin Steves.
1676259Sgreen * Copyright (c) 2001 Kevin Steves.  All rights reserved.
1776259Sgreen *
1876259Sgreen * Redistribution and use in source and binary forms, with or without
1976259Sgreen * modification, are permitted provided that the following conditions
2076259Sgreen * are met:
2176259Sgreen * 1. Redistributions of source code must retain the above copyright
2276259Sgreen *    notice, this list of conditions and the following disclaimer.
2376259Sgreen * 2. Redistributions in binary form must reproduce the above copyright
2476259Sgreen *    notice, this list of conditions and the following disclaimer in the
2576259Sgreen *    documentation and/or other materials provided with the distribution.
2676259Sgreen *
2776259Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2876259Sgreen * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2976259Sgreen * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
3076259Sgreen * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
3176259Sgreen * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
3276259Sgreen * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3376259Sgreen * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3476259Sgreen * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3576259Sgreen * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3676259Sgreen * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3776259Sgreen */
3876259Sgreen
3976259Sgreen/*
4076259Sgreen * Encoding and decoding of terminal modes in a portable way.
4176259Sgreen * Much of the format is defined in ttymodes.h; it is included multiple times
4276259Sgreen * into this file with the appropriate macro definitions to generate the
4376259Sgreen * suitable code.
4476259Sgreen */
4576259Sgreen
4657429Smarkm#include "includes.h"
4757429Smarkm
48162852Sdes#include <sys/types.h>
49162852Sdes
50162852Sdes#include <errno.h>
51162852Sdes#include <string.h>
52162852Sdes#include <termios.h>
53162852Sdes#include <stdarg.h>
54162852Sdes
5557429Smarkm#include "packet.h"
5676259Sgreen#include "log.h"
5776259Sgreen#include "compat.h"
5876259Sgreen#include "buffer.h"
5957429Smarkm
6076259Sgreen#define TTY_OP_END		0
6176259Sgreen/*
6276259Sgreen * uint32 (u_int) follows speed in SSH1 and SSH2
6376259Sgreen */
6476259Sgreen#define TTY_OP_ISPEED_PROTO1	192
6576259Sgreen#define TTY_OP_OSPEED_PROTO1	193
6676259Sgreen#define TTY_OP_ISPEED_PROTO2	128
6776259Sgreen#define TTY_OP_OSPEED_PROTO2	129
6857429Smarkm
6957429Smarkm/*
7057429Smarkm * Converts POSIX speed_t to a baud rate.  The values of the
7157429Smarkm * constants for speed_t are not themselves portable.
7257429Smarkm */
7360573Skrisstatic int
7457429Smarkmspeed_to_baud(speed_t speed)
7557429Smarkm{
7657429Smarkm	switch (speed) {
7757429Smarkm	case B0:
7857429Smarkm		return 0;
7957429Smarkm	case B50:
8057429Smarkm		return 50;
8157429Smarkm	case B75:
8257429Smarkm		return 75;
8357429Smarkm	case B110:
8457429Smarkm		return 110;
8557429Smarkm	case B134:
8657429Smarkm		return 134;
8757429Smarkm	case B150:
8857429Smarkm		return 150;
8957429Smarkm	case B200:
9057429Smarkm		return 200;
9157429Smarkm	case B300:
9257429Smarkm		return 300;
9357429Smarkm	case B600:
9457429Smarkm		return 600;
9557429Smarkm	case B1200:
9657429Smarkm		return 1200;
9757429Smarkm	case B1800:
9857429Smarkm		return 1800;
9957429Smarkm	case B2400:
10057429Smarkm		return 2400;
10157429Smarkm	case B4800:
10257429Smarkm		return 4800;
10357429Smarkm	case B9600:
10457429Smarkm		return 9600;
10557429Smarkm
10657429Smarkm#ifdef B19200
10757429Smarkm	case B19200:
10857429Smarkm		return 19200;
10957429Smarkm#else /* B19200 */
11057429Smarkm#ifdef EXTA
11157429Smarkm	case EXTA:
11257429Smarkm		return 19200;
11357429Smarkm#endif /* EXTA */
11457429Smarkm#endif /* B19200 */
11557429Smarkm
11657429Smarkm#ifdef B38400
11757429Smarkm	case B38400:
11857429Smarkm		return 38400;
11957429Smarkm#else /* B38400 */
12057429Smarkm#ifdef EXTB
12157429Smarkm	case EXTB:
12257429Smarkm		return 38400;
12357429Smarkm#endif /* EXTB */
12457429Smarkm#endif /* B38400 */
12557429Smarkm
12657429Smarkm#ifdef B7200
12757429Smarkm	case B7200:
12857429Smarkm		return 7200;
12957429Smarkm#endif /* B7200 */
13057429Smarkm#ifdef B14400
13157429Smarkm	case B14400:
13257429Smarkm		return 14400;
13357429Smarkm#endif /* B14400 */
13457429Smarkm#ifdef B28800
13557429Smarkm	case B28800:
13657429Smarkm		return 28800;
13757429Smarkm#endif /* B28800 */
13857429Smarkm#ifdef B57600
13957429Smarkm	case B57600:
14057429Smarkm		return 57600;
14157429Smarkm#endif /* B57600 */
14257429Smarkm#ifdef B76800
14357429Smarkm	case B76800:
14457429Smarkm		return 76800;
14557429Smarkm#endif /* B76800 */
14657429Smarkm#ifdef B115200
14757429Smarkm	case B115200:
14857429Smarkm		return 115200;
14957429Smarkm#endif /* B115200 */
15057429Smarkm#ifdef B230400
15157429Smarkm	case B230400:
15257429Smarkm		return 230400;
15357429Smarkm#endif /* B230400 */
15457429Smarkm	default:
15557429Smarkm		return 9600;
15657429Smarkm	}
15757429Smarkm}
15857429Smarkm
15957429Smarkm/*
16057429Smarkm * Converts a numeric baud rate to a POSIX speed_t.
16157429Smarkm */
16260573Skrisstatic speed_t
16357429Smarkmbaud_to_speed(int baud)
16457429Smarkm{
16557429Smarkm	switch (baud) {
16676259Sgreen	case 0:
16757429Smarkm		return B0;
16857429Smarkm	case 50:
16957429Smarkm		return B50;
17057429Smarkm	case 75:
17157429Smarkm		return B75;
17257429Smarkm	case 110:
17357429Smarkm		return B110;
17457429Smarkm	case 134:
17557429Smarkm		return B134;
17657429Smarkm	case 150:
17757429Smarkm		return B150;
17857429Smarkm	case 200:
17957429Smarkm		return B200;
18057429Smarkm	case 300:
18157429Smarkm		return B300;
18257429Smarkm	case 600:
18357429Smarkm		return B600;
18457429Smarkm	case 1200:
18557429Smarkm		return B1200;
18657429Smarkm	case 1800:
18757429Smarkm		return B1800;
18857429Smarkm	case 2400:
18957429Smarkm		return B2400;
19057429Smarkm	case 4800:
19157429Smarkm		return B4800;
19257429Smarkm	case 9600:
19357429Smarkm		return B9600;
19457429Smarkm
19557429Smarkm#ifdef B19200
19657429Smarkm	case 19200:
19757429Smarkm		return B19200;
19857429Smarkm#else /* B19200 */
19957429Smarkm#ifdef EXTA
20057429Smarkm	case 19200:
20157429Smarkm		return EXTA;
20257429Smarkm#endif /* EXTA */
20357429Smarkm#endif /* B19200 */
20457429Smarkm
20557429Smarkm#ifdef B38400
20657429Smarkm	case 38400:
20757429Smarkm		return B38400;
20857429Smarkm#else /* B38400 */
20957429Smarkm#ifdef EXTB
21057429Smarkm	case 38400:
21157429Smarkm		return EXTB;
21257429Smarkm#endif /* EXTB */
21357429Smarkm#endif /* B38400 */
21457429Smarkm
21557429Smarkm#ifdef B7200
21657429Smarkm	case 7200:
21757429Smarkm		return B7200;
21857429Smarkm#endif /* B7200 */
21957429Smarkm#ifdef B14400
22057429Smarkm	case 14400:
22157429Smarkm		return B14400;
22257429Smarkm#endif /* B14400 */
22357429Smarkm#ifdef B28800
22457429Smarkm	case 28800:
22557429Smarkm		return B28800;
22657429Smarkm#endif /* B28800 */
22757429Smarkm#ifdef B57600
22857429Smarkm	case 57600:
22957429Smarkm		return B57600;
23057429Smarkm#endif /* B57600 */
23157429Smarkm#ifdef B76800
23257429Smarkm	case 76800:
23357429Smarkm		return B76800;
23457429Smarkm#endif /* B76800 */
23557429Smarkm#ifdef B115200
23657429Smarkm	case 115200:
23757429Smarkm		return B115200;
23857429Smarkm#endif /* B115200 */
23957429Smarkm#ifdef B230400
24057429Smarkm	case 230400:
24157429Smarkm		return B230400;
24257429Smarkm#endif /* B230400 */
24357429Smarkm	default:
24457429Smarkm		return B9600;
24557429Smarkm	}
24657429Smarkm}
24757429Smarkm
24857429Smarkm/*
249149749Sdes * Encode a special character into SSH line format.
250149749Sdes */
251149749Sdesstatic u_int
252149749Sdesspecial_char_encode(cc_t c)
253149749Sdes{
254149749Sdes#ifdef _POSIX_VDISABLE
255149749Sdes	if (c == _POSIX_VDISABLE)
256149749Sdes		return 255;
257149749Sdes#endif /* _POSIX_VDISABLE */
258149749Sdes	return c;
259149749Sdes}
260149749Sdes
261149749Sdes/*
262149749Sdes * Decode a special character from SSH line format.
263149749Sdes */
264149749Sdesstatic cc_t
265149749Sdesspecial_char_decode(u_int c)
266149749Sdes{
267149749Sdes#ifdef _POSIX_VDISABLE
268149749Sdes	if (c == 255)
269149749Sdes		return _POSIX_VDISABLE;
270149749Sdes#endif /* _POSIX_VDISABLE */
271149749Sdes	return c;
272149749Sdes}
273149749Sdes
274149749Sdes/*
27557429Smarkm * Encodes terminal modes for the terminal referenced by fd
27676259Sgreen * or tiop in a portable manner, and appends the modes to a packet
27757429Smarkm * being constructed.
27857429Smarkm */
27960573Skrisvoid
28076259Sgreentty_make_modes(int fd, struct termios *tiop)
28157429Smarkm{
28257429Smarkm	struct termios tio;
28357429Smarkm	int baud;
28476259Sgreen	Buffer buf;
28576259Sgreen	int tty_op_ospeed, tty_op_ispeed;
28676259Sgreen	void (*put_arg)(Buffer *, u_int);
28757429Smarkm
28876259Sgreen	buffer_init(&buf);
28976259Sgreen	if (compat20) {
29076259Sgreen		tty_op_ospeed = TTY_OP_OSPEED_PROTO2;
29176259Sgreen		tty_op_ispeed = TTY_OP_ISPEED_PROTO2;
29276259Sgreen		put_arg = buffer_put_int;
29376259Sgreen	} else {
29476259Sgreen		tty_op_ospeed = TTY_OP_OSPEED_PROTO1;
29576259Sgreen		tty_op_ispeed = TTY_OP_ISPEED_PROTO1;
29676259Sgreen		put_arg = (void (*)(Buffer *, u_int)) buffer_put_char;
29757429Smarkm	}
29876259Sgreen
29976259Sgreen	if (tiop == NULL) {
300181111Sdes		if (fd == -1) {
301181111Sdes			debug("tty_make_modes: no fd or tio");
302181111Sdes			goto end;
303181111Sdes		}
30476259Sgreen		if (tcgetattr(fd, &tio) == -1) {
305124208Sdes			logit("tcgetattr: %.100s", strerror(errno));
30676259Sgreen			goto end;
30776259Sgreen		}
30876259Sgreen	} else
30976259Sgreen		tio = *tiop;
31076259Sgreen
31157429Smarkm	/* Store input and output baud rates. */
31257429Smarkm	baud = speed_to_baud(cfgetospeed(&tio));
31376259Sgreen	buffer_put_char(&buf, tty_op_ospeed);
31476259Sgreen	buffer_put_int(&buf, baud);
31557429Smarkm	baud = speed_to_baud(cfgetispeed(&tio));
31676259Sgreen	buffer_put_char(&buf, tty_op_ispeed);
31776259Sgreen	buffer_put_int(&buf, baud);
31857429Smarkm
31957429Smarkm	/* Store values of mode flags. */
32057429Smarkm#define TTYCHAR(NAME, OP) \
32176259Sgreen	buffer_put_char(&buf, OP); \
322149749Sdes	put_arg(&buf, special_char_encode(tio.c_cc[NAME]));
32376259Sgreen
32457429Smarkm#define TTYMODE(NAME, FIELD, OP) \
32576259Sgreen	buffer_put_char(&buf, OP); \
32676259Sgreen	put_arg(&buf, ((tio.FIELD & NAME) != 0));
32757429Smarkm
32857429Smarkm#include "ttymodes.h"
32957429Smarkm
33057429Smarkm#undef TTYCHAR
33157429Smarkm#undef TTYMODE
33257429Smarkm
33376259Sgreenend:
33457429Smarkm	/* Mark end of mode data. */
33576259Sgreen	buffer_put_char(&buf, TTY_OP_END);
33676259Sgreen	if (compat20)
33776259Sgreen		packet_put_string(buffer_ptr(&buf), buffer_len(&buf));
33876259Sgreen	else
33976259Sgreen		packet_put_raw(buffer_ptr(&buf), buffer_len(&buf));
34076259Sgreen	buffer_free(&buf);
34157429Smarkm}
34257429Smarkm
34357429Smarkm/*
34457429Smarkm * Decodes terminal modes for the terminal referenced by fd in a portable
34557429Smarkm * manner from a packet being read.
34657429Smarkm */
34760573Skrisvoid
34857429Smarkmtty_parse_modes(int fd, int *n_bytes_ptr)
34957429Smarkm{
35057429Smarkm	struct termios tio;
35157429Smarkm	int opcode, baud;
35257429Smarkm	int n_bytes = 0;
35357429Smarkm	int failure = 0;
35476259Sgreen	u_int (*get_arg)(void);
355181111Sdes	int arg_size;
35657429Smarkm
35776259Sgreen	if (compat20) {
35876259Sgreen		*n_bytes_ptr = packet_get_int();
35976259Sgreen		if (*n_bytes_ptr == 0)
36076259Sgreen			return;
36176259Sgreen		get_arg = packet_get_int;
36276259Sgreen		arg_size = 4;
36376259Sgreen	} else {
36476259Sgreen		get_arg = packet_get_char;
36576259Sgreen		arg_size = 1;
36676259Sgreen	}
36776259Sgreen
36857429Smarkm	/*
36957429Smarkm	 * Get old attributes for the terminal.  We will modify these
37057429Smarkm	 * flags. I am hoping that if there are any machine-specific
37157429Smarkm	 * modes, they will initially have reasonable values.
37257429Smarkm	 */
37376259Sgreen	if (tcgetattr(fd, &tio) == -1) {
374124208Sdes		logit("tcgetattr: %.100s", strerror(errno));
37557429Smarkm		failure = -1;
37676259Sgreen	}
37757429Smarkm
37857429Smarkm	for (;;) {
37957429Smarkm		n_bytes += 1;
38057429Smarkm		opcode = packet_get_char();
38157429Smarkm		switch (opcode) {
38257429Smarkm		case TTY_OP_END:
38357429Smarkm			goto set;
38457429Smarkm
38576259Sgreen		/* XXX: future conflict possible */
38676259Sgreen		case TTY_OP_ISPEED_PROTO1:
38776259Sgreen		case TTY_OP_ISPEED_PROTO2:
38857429Smarkm			n_bytes += 4;
38957429Smarkm			baud = packet_get_int();
390162852Sdes			if (failure != -1 &&
391162852Sdes			    cfsetispeed(&tio, baud_to_speed(baud)) == -1)
39257429Smarkm				error("cfsetispeed failed for %d", baud);
39357429Smarkm			break;
39457429Smarkm
39576259Sgreen		/* XXX: future conflict possible */
39676259Sgreen		case TTY_OP_OSPEED_PROTO1:
39776259Sgreen		case TTY_OP_OSPEED_PROTO2:
39857429Smarkm			n_bytes += 4;
39957429Smarkm			baud = packet_get_int();
400162852Sdes			if (failure != -1 &&
401162852Sdes			    cfsetospeed(&tio, baud_to_speed(baud)) == -1)
40257429Smarkm				error("cfsetospeed failed for %d", baud);
40357429Smarkm			break;
40457429Smarkm
40576259Sgreen#define TTYCHAR(NAME, OP) \
40676259Sgreen	case OP: \
40776259Sgreen	  n_bytes += arg_size; \
408149749Sdes	  tio.c_cc[NAME] = special_char_decode(get_arg()); \
40957429Smarkm	  break;
41076259Sgreen#define TTYMODE(NAME, FIELD, OP) \
41176259Sgreen	case OP: \
41276259Sgreen	  n_bytes += arg_size; \
413181111Sdes	  if (get_arg()) \
41476259Sgreen	    tio.FIELD |= NAME; \
41576259Sgreen	  else \
41676259Sgreen	    tio.FIELD &= ~NAME;	\
41757429Smarkm	  break;
41857429Smarkm
41957429Smarkm#include "ttymodes.h"
42057429Smarkm
42157429Smarkm#undef TTYCHAR
42257429Smarkm#undef TTYMODE
42357429Smarkm
42457429Smarkm		default:
42557429Smarkm			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
42692555Sdes			    opcode, opcode);
42776259Sgreen			if (!compat20) {
42876259Sgreen				/*
42976259Sgreen				 * SSH1:
43076259Sgreen				 * Opcodes 1 to 127 are defined to have
43176259Sgreen				 * a one-byte argument.
43298675Sdes				 * Opcodes 128 to 159 are defined to have
43398675Sdes				 * an integer argument.
43498675Sdes				 */
43576259Sgreen				if (opcode > 0 && opcode < 128) {
43676259Sgreen					n_bytes += 1;
43776259Sgreen					(void) packet_get_char();
43876259Sgreen					break;
43976259Sgreen				} else if (opcode >= 128 && opcode < 160) {
44098675Sdes					n_bytes += 4;
44198675Sdes					(void) packet_get_int();
44298675Sdes					break;
44376259Sgreen				} else {
44476259Sgreen					/*
44576259Sgreen					 * It is a truly undefined opcode (160 to 255).
44676259Sgreen					 * We have no idea about its arguments.  So we
447162852Sdes					 * must stop parsing.  Note that some data
448162852Sdes					 * may be left in the packet; hopefully there
449162852Sdes					 * is nothing more coming after the mode data.
45076259Sgreen					 */
451162852Sdes					logit("parse_tty_modes: unknown opcode %d",
452162852Sdes					    opcode);
45376259Sgreen					goto set;
45498675Sdes				}
45557429Smarkm			} else {
45657429Smarkm				/*
45776259Sgreen				 * SSH2:
45876259Sgreen				 * Opcodes 1 to 159 are defined to have
45976259Sgreen				 * a uint32 argument.
46076259Sgreen				 * Opcodes 160 to 255 are undefined and
46176259Sgreen				 * cause parsing to stop.
46257429Smarkm				 */
46376259Sgreen				if (opcode > 0 && opcode < 160) {
46457429Smarkm					n_bytes += 4;
46557429Smarkm					(void) packet_get_int();
46657429Smarkm					break;
46776259Sgreen				} else {
468162852Sdes					logit("parse_tty_modes: unknown opcode %d",
469162852Sdes					    opcode);
47076259Sgreen					goto set;
47157429Smarkm				}
47298675Sdes			}
47357429Smarkm		}
47457429Smarkm	}
47557429Smarkm
47657429Smarkmset:
47757429Smarkm	if (*n_bytes_ptr != n_bytes) {
47857429Smarkm		*n_bytes_ptr = n_bytes;
479124208Sdes		logit("parse_tty_modes: n_bytes_ptr != n_bytes: %d %d",
48076259Sgreen		    *n_bytes_ptr, n_bytes);
48157429Smarkm		return;		/* Don't process bytes passed */
48257429Smarkm	}
48357429Smarkm	if (failure == -1)
48476259Sgreen		return;		/* Packet parsed ok but tcgetattr() failed */
48557429Smarkm
48657429Smarkm	/* Set the new modes for the terminal. */
48776259Sgreen	if (tcsetattr(fd, TCSANOW, &tio) == -1)
488124208Sdes		logit("Setting tty modes failed: %.100s", strerror(errno));
48957429Smarkm}
490