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