1*17418e98Schristos /* $NetBSD: ttymodes.c,v 1.12 2021/03/05 17:47:16 christos Exp $ */
2*17418e98Schristos /* $OpenBSD: ttymodes.c,v 1.36 2021/01/27 09:26:54 djm Exp $ */
355a4608bSchristos
4ca32bd8dSchristos /*
5ca32bd8dSchristos * Author: Tatu Ylonen <ylo@cs.hut.fi>
6ca32bd8dSchristos * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
7ca32bd8dSchristos * All rights reserved
8ca32bd8dSchristos *
9ca32bd8dSchristos * As far as I am concerned, the code I have written for this software
10ca32bd8dSchristos * can be used freely for any purpose. Any derived versions of this
11ca32bd8dSchristos * software must be clearly marked as such, and if the derived work is
12ca32bd8dSchristos * incompatible with the protocol description in the RFC file, it must be
13ca32bd8dSchristos * called by a name other than "ssh" or "Secure Shell".
14ca32bd8dSchristos */
15ca32bd8dSchristos
16ca32bd8dSchristos /*
17ca32bd8dSchristos * SSH2 tty modes support by Kevin Steves.
18ca32bd8dSchristos * Copyright (c) 2001 Kevin Steves. All rights reserved.
19ca32bd8dSchristos *
20ca32bd8dSchristos * Redistribution and use in source and binary forms, with or without
21ca32bd8dSchristos * modification, are permitted provided that the following conditions
22ca32bd8dSchristos * are met:
23ca32bd8dSchristos * 1. Redistributions of source code must retain the above copyright
24ca32bd8dSchristos * notice, this list of conditions and the following disclaimer.
25ca32bd8dSchristos * 2. Redistributions in binary form must reproduce the above copyright
26ca32bd8dSchristos * notice, this list of conditions and the following disclaimer in the
27ca32bd8dSchristos * documentation and/or other materials provided with the distribution.
28ca32bd8dSchristos *
29ca32bd8dSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
30ca32bd8dSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
31ca32bd8dSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
32ca32bd8dSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
33ca32bd8dSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
34ca32bd8dSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35ca32bd8dSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36ca32bd8dSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37ca32bd8dSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
38ca32bd8dSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39ca32bd8dSchristos */
40ca32bd8dSchristos
41ca32bd8dSchristos /*
42ca32bd8dSchristos * Encoding and decoding of terminal modes in a portable way.
43ca32bd8dSchristos * Much of the format is defined in ttymodes.h; it is included multiple times
44ca32bd8dSchristos * into this file with the appropriate macro definitions to generate the
45ca32bd8dSchristos * suitable code.
46ca32bd8dSchristos */
47ca32bd8dSchristos
48313c6c94Schristos #include "includes.h"
49*17418e98Schristos __RCSID("$NetBSD: ttymodes.c,v 1.12 2021/03/05 17:47:16 christos Exp $");
50ca32bd8dSchristos #include <sys/types.h>
51ca32bd8dSchristos
52ca32bd8dSchristos #include <errno.h>
53ca32bd8dSchristos #include <string.h>
54ca32bd8dSchristos #include <termios.h>
55ca32bd8dSchristos #include <stdarg.h>
56ca32bd8dSchristos
57ca32bd8dSchristos #include "packet.h"
58ca32bd8dSchristos #include "log.h"
59ca32bd8dSchristos #include "compat.h"
6055a4608bSchristos #include "sshbuf.h"
6155a4608bSchristos #include "ssherr.h"
62ca32bd8dSchristos
63ca32bd8dSchristos #define TTY_OP_END 0
64ca32bd8dSchristos /*
657a183406Schristos * uint32 (u_int) follows speed.
66ca32bd8dSchristos */
677a183406Schristos #define TTY_OP_ISPEED 128
687a183406Schristos #define TTY_OP_OSPEED 129
69ca32bd8dSchristos
70ca32bd8dSchristos /*
71ca32bd8dSchristos * Converts POSIX speed_t to a baud rate. The values of the
72ca32bd8dSchristos * constants for speed_t are not themselves portable.
73ca32bd8dSchristos */
74ca32bd8dSchristos static int
speed_to_baud(speed_t speed)75ca32bd8dSchristos speed_to_baud(speed_t speed)
76ca32bd8dSchristos {
77ca32bd8dSchristos switch (speed) {
78ca32bd8dSchristos case B0:
79ca32bd8dSchristos return 0;
80ca32bd8dSchristos case B50:
81ca32bd8dSchristos return 50;
82ca32bd8dSchristos case B75:
83ca32bd8dSchristos return 75;
84ca32bd8dSchristos case B110:
85ca32bd8dSchristos return 110;
86ca32bd8dSchristos case B134:
87ca32bd8dSchristos return 134;
88ca32bd8dSchristos case B150:
89ca32bd8dSchristos return 150;
90ca32bd8dSchristos case B200:
91ca32bd8dSchristos return 200;
92ca32bd8dSchristos case B300:
93ca32bd8dSchristos return 300;
94ca32bd8dSchristos case B600:
95ca32bd8dSchristos return 600;
96ca32bd8dSchristos case B1200:
97ca32bd8dSchristos return 1200;
98ca32bd8dSchristos case B1800:
99ca32bd8dSchristos return 1800;
100ca32bd8dSchristos case B2400:
101ca32bd8dSchristos return 2400;
102ca32bd8dSchristos case B4800:
103ca32bd8dSchristos return 4800;
104ca32bd8dSchristos case B9600:
105ca32bd8dSchristos return 9600;
106ca32bd8dSchristos
107ca32bd8dSchristos #ifdef B19200
108ca32bd8dSchristos case B19200:
109ca32bd8dSchristos return 19200;
110ca32bd8dSchristos #else /* B19200 */
111ca32bd8dSchristos #ifdef EXTA
112ca32bd8dSchristos case EXTA:
113ca32bd8dSchristos return 19200;
114ca32bd8dSchristos #endif /* EXTA */
115ca32bd8dSchristos #endif /* B19200 */
116ca32bd8dSchristos
117ca32bd8dSchristos #ifdef B38400
118ca32bd8dSchristos case B38400:
119ca32bd8dSchristos return 38400;
120ca32bd8dSchristos #else /* B38400 */
121ca32bd8dSchristos #ifdef EXTB
122ca32bd8dSchristos case EXTB:
123ca32bd8dSchristos return 38400;
124ca32bd8dSchristos #endif /* EXTB */
125ca32bd8dSchristos #endif /* B38400 */
126ca32bd8dSchristos
127ca32bd8dSchristos #ifdef B7200
128ca32bd8dSchristos case B7200:
129ca32bd8dSchristos return 7200;
130ca32bd8dSchristos #endif /* B7200 */
131ca32bd8dSchristos #ifdef B14400
132ca32bd8dSchristos case B14400:
133ca32bd8dSchristos return 14400;
134ca32bd8dSchristos #endif /* B14400 */
135ca32bd8dSchristos #ifdef B28800
136ca32bd8dSchristos case B28800:
137ca32bd8dSchristos return 28800;
138ca32bd8dSchristos #endif /* B28800 */
139ca32bd8dSchristos #ifdef B57600
140ca32bd8dSchristos case B57600:
141ca32bd8dSchristos return 57600;
142ca32bd8dSchristos #endif /* B57600 */
143ca32bd8dSchristos #ifdef B76800
144ca32bd8dSchristos case B76800:
145ca32bd8dSchristos return 76800;
146ca32bd8dSchristos #endif /* B76800 */
147ca32bd8dSchristos #ifdef B115200
148ca32bd8dSchristos case B115200:
149ca32bd8dSchristos return 115200;
150ca32bd8dSchristos #endif /* B115200 */
151ca32bd8dSchristos #ifdef B230400
152ca32bd8dSchristos case B230400:
153ca32bd8dSchristos return 230400;
154ca32bd8dSchristos #endif /* B230400 */
155ca32bd8dSchristos default:
156ca32bd8dSchristos return 9600;
157ca32bd8dSchristos }
158ca32bd8dSchristos }
159ca32bd8dSchristos
160ca32bd8dSchristos /*
161ca32bd8dSchristos * Converts a numeric baud rate to a POSIX speed_t.
162ca32bd8dSchristos */
163ca32bd8dSchristos static speed_t
baud_to_speed(int baud)164ca32bd8dSchristos baud_to_speed(int baud)
165ca32bd8dSchristos {
166ca32bd8dSchristos switch (baud) {
167ca32bd8dSchristos case 0:
168ca32bd8dSchristos return B0;
169ca32bd8dSchristos case 50:
170ca32bd8dSchristos return B50;
171ca32bd8dSchristos case 75:
172ca32bd8dSchristos return B75;
173ca32bd8dSchristos case 110:
174ca32bd8dSchristos return B110;
175ca32bd8dSchristos case 134:
176ca32bd8dSchristos return B134;
177ca32bd8dSchristos case 150:
178ca32bd8dSchristos return B150;
179ca32bd8dSchristos case 200:
180ca32bd8dSchristos return B200;
181ca32bd8dSchristos case 300:
182ca32bd8dSchristos return B300;
183ca32bd8dSchristos case 600:
184ca32bd8dSchristos return B600;
185ca32bd8dSchristos case 1200:
186ca32bd8dSchristos return B1200;
187ca32bd8dSchristos case 1800:
188ca32bd8dSchristos return B1800;
189ca32bd8dSchristos case 2400:
190ca32bd8dSchristos return B2400;
191ca32bd8dSchristos case 4800:
192ca32bd8dSchristos return B4800;
193ca32bd8dSchristos case 9600:
194ca32bd8dSchristos return B9600;
195ca32bd8dSchristos
196ca32bd8dSchristos #ifdef B19200
197ca32bd8dSchristos case 19200:
198ca32bd8dSchristos return B19200;
199ca32bd8dSchristos #else /* B19200 */
200ca32bd8dSchristos #ifdef EXTA
201ca32bd8dSchristos case 19200:
202ca32bd8dSchristos return EXTA;
203ca32bd8dSchristos #endif /* EXTA */
204ca32bd8dSchristos #endif /* B19200 */
205ca32bd8dSchristos
206ca32bd8dSchristos #ifdef B38400
207ca32bd8dSchristos case 38400:
208ca32bd8dSchristos return B38400;
209ca32bd8dSchristos #else /* B38400 */
210ca32bd8dSchristos #ifdef EXTB
211ca32bd8dSchristos case 38400:
212ca32bd8dSchristos return EXTB;
213ca32bd8dSchristos #endif /* EXTB */
214ca32bd8dSchristos #endif /* B38400 */
215ca32bd8dSchristos
216ca32bd8dSchristos #ifdef B7200
217ca32bd8dSchristos case 7200:
218ca32bd8dSchristos return B7200;
219ca32bd8dSchristos #endif /* B7200 */
220ca32bd8dSchristos #ifdef B14400
221ca32bd8dSchristos case 14400:
222ca32bd8dSchristos return B14400;
223ca32bd8dSchristos #endif /* B14400 */
224ca32bd8dSchristos #ifdef B28800
225ca32bd8dSchristos case 28800:
226ca32bd8dSchristos return B28800;
227ca32bd8dSchristos #endif /* B28800 */
228ca32bd8dSchristos #ifdef B57600
229ca32bd8dSchristos case 57600:
230ca32bd8dSchristos return B57600;
231ca32bd8dSchristos #endif /* B57600 */
232ca32bd8dSchristos #ifdef B76800
233ca32bd8dSchristos case 76800:
234ca32bd8dSchristos return B76800;
235ca32bd8dSchristos #endif /* B76800 */
236ca32bd8dSchristos #ifdef B115200
237ca32bd8dSchristos case 115200:
238ca32bd8dSchristos return B115200;
239ca32bd8dSchristos #endif /* B115200 */
240ca32bd8dSchristos #ifdef B230400
241ca32bd8dSchristos case 230400:
242ca32bd8dSchristos return B230400;
243ca32bd8dSchristos #endif /* B230400 */
244ca32bd8dSchristos default:
245ca32bd8dSchristos return B9600;
246ca32bd8dSchristos }
247ca32bd8dSchristos }
248ca32bd8dSchristos
249ca32bd8dSchristos /*
250ca32bd8dSchristos * Encodes terminal modes for the terminal referenced by fd
251ca32bd8dSchristos * or tiop in a portable manner, and appends the modes to a packet
252ca32bd8dSchristos * being constructed.
253ca32bd8dSchristos */
254ca32bd8dSchristos void
ssh_tty_make_modes(struct ssh * ssh,int fd,struct termios * tiop)25555a4608bSchristos ssh_tty_make_modes(struct ssh *ssh, int fd, struct termios *tiop)
256ca32bd8dSchristos {
257ca32bd8dSchristos struct termios tio;
25855a4608bSchristos struct sshbuf *buf;
25955a4608bSchristos int r, ibaud, obaud;
260ca32bd8dSchristos
26155a4608bSchristos if ((buf = sshbuf_new()) == NULL)
262*17418e98Schristos fatal_f("sshbuf_new failed");
263ca32bd8dSchristos
264ca32bd8dSchristos if (tiop == NULL) {
265ca32bd8dSchristos if (fd == -1) {
266*17418e98Schristos debug_f("no fd or tio");
267ca32bd8dSchristos goto end;
268ca32bd8dSchristos }
269ca32bd8dSchristos if (tcgetattr(fd, &tio) == -1) {
270ca32bd8dSchristos logit("tcgetattr: %.100s", strerror(errno));
271ca32bd8dSchristos goto end;
272ca32bd8dSchristos }
273ca32bd8dSchristos } else
274ca32bd8dSchristos tio = *tiop;
275ca32bd8dSchristos
276ca32bd8dSchristos /* Store input and output baud rates. */
27755a4608bSchristos obaud = speed_to_baud(cfgetospeed(&tio));
27855a4608bSchristos ibaud = speed_to_baud(cfgetispeed(&tio));
27955a4608bSchristos if ((r = sshbuf_put_u8(buf, TTY_OP_OSPEED)) != 0 ||
28055a4608bSchristos (r = sshbuf_put_u32(buf, obaud)) != 0 ||
28155a4608bSchristos (r = sshbuf_put_u8(buf, TTY_OP_ISPEED)) != 0 ||
28255a4608bSchristos (r = sshbuf_put_u32(buf, ibaud)) != 0)
283*17418e98Schristos fatal_fr(r, "compose");
284ca32bd8dSchristos
285ca32bd8dSchristos /* Store values of mode flags. */
286ca32bd8dSchristos #define TTYCHAR(NAME, OP) \
28755a4608bSchristos if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
28855a4608bSchristos (r = sshbuf_put_u32(buf, tio.c_cc[NAME])) != 0) \
289*17418e98Schristos fatal_fr(r, "compose %s", #NAME);
290ca32bd8dSchristos
291ffae97bbSchristos #define SSH_TTYMODE_IUTF8 42 /* for SSH_BUG_UTF8TTYMODE */
292ffae97bbSchristos
293ca32bd8dSchristos #define TTYMODE(NAME, FIELD, OP) \
294*17418e98Schristos if (OP == SSH_TTYMODE_IUTF8 && (ssh->compat & SSH_BUG_UTF8TTYMODE)) { \
295*17418e98Schristos debug3_f("SSH_BUG_UTF8TTYMODE"); \
29655a4608bSchristos } else if ((r = sshbuf_put_u8(buf, OP)) != 0 || \
29755a4608bSchristos (r = sshbuf_put_u32(buf, ((tio.FIELD & NAME) != 0))) != 0) \
298*17418e98Schristos fatal_fr(r, "compose %s", #NAME);
299ca32bd8dSchristos
300ca32bd8dSchristos #include "ttymodes.h"
301ca32bd8dSchristos
302ca32bd8dSchristos #undef TTYCHAR
303ca32bd8dSchristos #undef TTYMODE
304ca32bd8dSchristos
305ca32bd8dSchristos end:
306ca32bd8dSchristos /* Mark end of mode data. */
30755a4608bSchristos if ((r = sshbuf_put_u8(buf, TTY_OP_END)) != 0 ||
30855a4608bSchristos (r = sshpkt_put_stringb(ssh, buf)) != 0)
309*17418e98Schristos fatal_fr(r, "compose end");
31055a4608bSchristos sshbuf_free(buf);
311ca32bd8dSchristos }
312ca32bd8dSchristos
313ca32bd8dSchristos /*
314ca32bd8dSchristos * Decodes terminal modes for the terminal referenced by fd in a portable
315ca32bd8dSchristos * manner from a packet being read.
316ca32bd8dSchristos */
317ca32bd8dSchristos void
ssh_tty_parse_modes(struct ssh * ssh,int fd)31855a4608bSchristos ssh_tty_parse_modes(struct ssh *ssh, int fd)
319ca32bd8dSchristos {
320ca32bd8dSchristos struct termios tio;
32155a4608bSchristos struct sshbuf *buf;
32255a4608bSchristos const u_char *data;
32355a4608bSchristos u_char opcode;
32455a4608bSchristos u_int baud, u;
32555a4608bSchristos int r, failure = 0;
32655a4608bSchristos size_t len;
327ca32bd8dSchristos
32855a4608bSchristos if ((r = sshpkt_get_string_direct(ssh, &data, &len)) != 0)
329*17418e98Schristos fatal_fr(r, "parse");
33055a4608bSchristos if (len == 0)
331ca32bd8dSchristos return;
33255a4608bSchristos if ((buf = sshbuf_from(data, len)) == NULL) {
333*17418e98Schristos error_f("sshbuf_from failed");
33455a4608bSchristos return;
33555a4608bSchristos }
336ca32bd8dSchristos
337ca32bd8dSchristos /*
338ca32bd8dSchristos * Get old attributes for the terminal. We will modify these
339ca32bd8dSchristos * flags. I am hoping that if there are any machine-specific
340ca32bd8dSchristos * modes, they will initially have reasonable values.
341ca32bd8dSchristos */
342ca32bd8dSchristos if (tcgetattr(fd, &tio) == -1) {
343ca32bd8dSchristos logit("tcgetattr: %.100s", strerror(errno));
344ca32bd8dSchristos failure = -1;
345ca32bd8dSchristos }
346ca32bd8dSchristos
34755a4608bSchristos while (sshbuf_len(buf) > 0) {
34855a4608bSchristos if ((r = sshbuf_get_u8(buf, &opcode)) != 0)
349*17418e98Schristos fatal_fr(r, "parse opcode");
350ca32bd8dSchristos switch (opcode) {
351ca32bd8dSchristos case TTY_OP_END:
352ca32bd8dSchristos goto set;
353ca32bd8dSchristos
3547a183406Schristos case TTY_OP_ISPEED:
35555a4608bSchristos if ((r = sshbuf_get_u32(buf, &baud)) != 0)
356*17418e98Schristos fatal_fr(r, "parse ispeed");
357ca32bd8dSchristos if (failure != -1 &&
358ca32bd8dSchristos cfsetispeed(&tio, baud_to_speed(baud)) == -1)
359ca32bd8dSchristos error("cfsetispeed failed for %d", baud);
360ca32bd8dSchristos break;
361ca32bd8dSchristos
3627a183406Schristos case TTY_OP_OSPEED:
36355a4608bSchristos if ((r = sshbuf_get_u32(buf, &baud)) != 0)
364*17418e98Schristos fatal_fr(r, "parse ospeed");
365ca32bd8dSchristos if (failure != -1 &&
366ca32bd8dSchristos cfsetospeed(&tio, baud_to_speed(baud)) == -1)
367ca32bd8dSchristos error("cfsetospeed failed for %d", baud);
368ca32bd8dSchristos break;
369ca32bd8dSchristos
370ca32bd8dSchristos #define TTYCHAR(NAME, OP) \
371ca32bd8dSchristos case OP: \
37255a4608bSchristos if ((r = sshbuf_get_u32(buf, &u)) != 0) \
373*17418e98Schristos fatal_fr(r, "parse %s", #NAME); \
37455a4608bSchristos tio.c_cc[NAME] = u; \
375ca32bd8dSchristos break;
376ca32bd8dSchristos #define TTYMODE(NAME, FIELD, OP) \
377ca32bd8dSchristos case OP: \
37855a4608bSchristos if ((r = sshbuf_get_u32(buf, &u)) != 0) \
379*17418e98Schristos fatal_fr(r, "parse %s", #NAME); \
38055a4608bSchristos if (u) \
381ca32bd8dSchristos tio.FIELD |= NAME; \
382ca32bd8dSchristos else \
383ca32bd8dSchristos tio.FIELD &= ~NAME; \
384ca32bd8dSchristos break;
385ca32bd8dSchristos
386ca32bd8dSchristos #include "ttymodes.h"
387ca32bd8dSchristos
388ca32bd8dSchristos #undef TTYCHAR
389ca32bd8dSchristos #undef TTYMODE
390ca32bd8dSchristos
391ca32bd8dSchristos default:
392ca32bd8dSchristos debug("Ignoring unsupported tty mode opcode %d (0x%x)",
393ca32bd8dSchristos opcode, opcode);
394ca32bd8dSchristos /*
395ca32bd8dSchristos * SSH2:
3967a183406Schristos * Opcodes 1 to 159 are defined to have a uint32
3977a183406Schristos * argument.
3987a183406Schristos * Opcodes 160 to 255 are undefined and cause parsing
3997a183406Schristos * to stop.
400ca32bd8dSchristos */
401ca32bd8dSchristos if (opcode > 0 && opcode < 160) {
40255a4608bSchristos if ((r = sshbuf_get_u32(buf, NULL)) != 0)
403*17418e98Schristos fatal_fr(r, "parse arg");
404ca32bd8dSchristos break;
405ca32bd8dSchristos } else {
406*17418e98Schristos logit_f("unknown opcode %d", opcode);
407ca32bd8dSchristos goto set;
408ca32bd8dSchristos }
409ca32bd8dSchristos }
410ca32bd8dSchristos }
411ca32bd8dSchristos
412ca32bd8dSchristos set:
41355a4608bSchristos len = sshbuf_len(buf);
41455a4608bSchristos sshbuf_free(buf);
41555a4608bSchristos if (len > 0) {
416*17418e98Schristos logit_f("%zu bytes left", len);
417ca32bd8dSchristos return; /* Don't process bytes passed */
418ca32bd8dSchristos }
419ca32bd8dSchristos if (failure == -1)
420ca32bd8dSchristos return; /* Packet parsed ok but tcgetattr() failed */
421ca32bd8dSchristos
422ca32bd8dSchristos /* Set the new modes for the terminal. */
423ca32bd8dSchristos if (tcsetattr(fd, TCSANOW, &tio) == -1)
424ca32bd8dSchristos logit("Setting tty modes failed: %.100s", strerror(errno));
425ca32bd8dSchristos }
426