1/* $OpenBSD: ttymodes.c,v 1.34 2018/07/09 21:20:26 markus 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("%s: sshbuf_new failed", __func__);
287
288	if (tiop == NULL) {
289		if (fd == -1) {
290			debug("%s: no fd or tio", __func__);
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("%s: buffer error: %s", __func__, ssh_err(r));
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("%s: buffer error: %s", __func__, ssh_err(r)); \
315
316#define SSH_TTYMODE_IUTF8 42  /* for SSH_BUG_UTF8TTYMODE */
317
318#define TTYMODE(NAME, FIELD, OP) \
319	if (OP == SSH_TTYMODE_IUTF8 && (datafellows & SSH_BUG_UTF8TTYMODE)) { \
320		debug3("%s: SSH_BUG_UTF8TTYMODE", __func__); \
321	} else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
322	    (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \
323		fatal("%s: buffer error: %s", __func__, ssh_err(r)); \
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("%s: packet error: %s", __func__, ssh_err(r));
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("%s: packet error: %s", __func__, ssh_err(r));
355	if (len == 0)
356		return;
357	if ((buf = sshbuf_from(data, len)) == NULL) {
358		error("%s: sshbuf_from failed", __func__);
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("%s: packet error: %s", __func__, ssh_err(r));
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("%s: packet error: %s",
382				    __func__, ssh_err(r));
383			if (failure != -1 &&
384			    cfsetispeed(&tio, baud_to_speed(baud)) == -1)
385				error("cfsetispeed failed for %d", baud);
386			break;
387
388		case TTY_OP_OSPEED:
389			if ((r = sshbuf_get_u32(buf, &baud)) != 0)
390				fatal("%s: packet error: %s",
391				    __func__, ssh_err(r));
392			if (failure != -1 &&
393			    cfsetospeed(&tio, baud_to_speed(baud)) == -1)
394				error("cfsetospeed failed for %d", baud);
395			break;
396
397#define TTYCHAR(NAME, OP) \
398		case OP: \
399			if ((r = sshbuf_get_u32(buf, &u)) != 0) \
400				fatal("%s: packet error: %s", __func__, \
401				    ssh_err(r)); \
402			tio.c_cc[NAME] = special_char_decode(u); \
403			break;
404#define TTYMODE(NAME, FIELD, OP) \
405		case OP: \
406			if ((r = sshbuf_get_u32(buf, &u)) != 0) \
407				fatal("%s: packet error: %s", __func__, \
408				    ssh_err(r)); \
409			if (u) \
410				tio.FIELD |= NAME; \
411			else \
412				tio.FIELD &= ~NAME; \
413			break;
414
415#include "ttymodes.h"
416
417#undef TTYCHAR
418#undef TTYMODE
419
420		default:
421			debug("Ignoring unsupported tty mode opcode %d (0x%x)",
422			    opcode, opcode);
423			/*
424			 * SSH2:
425			 * Opcodes 1 to 159 are defined to have a uint32
426			 * argument.
427			 * Opcodes 160 to 255 are undefined and cause parsing
428			 * to stop.
429			 */
430			if (opcode > 0 && opcode < 160) {
431				if ((r = sshbuf_get_u32(buf, NULL)) != 0)
432					fatal("%s: packet error: %s", __func__,
433					    ssh_err(r));
434				break;
435			} else {
436				logit("%s: unknown opcode %d", __func__,
437				    opcode);
438				goto set;
439			}
440		}
441	}
442
443set:
444	len = sshbuf_len(buf);
445	sshbuf_free(buf);
446	if (len > 0) {
447		logit("%s: %zu bytes left", __func__, len);
448		return;		/* Don't process bytes passed */
449	}
450	if (failure == -1)
451		return;		/* Packet parsed ok but tcgetattr() failed */
452
453	/* Set the new modes for the terminal. */
454	if (tcsetattr(fd, TCSANOW, &tio) == -1)
455		logit("Setting tty modes failed: %.100s", strerror(errno));
456}
457