libsodium: Needed for Dnscrypto-proxy Release 1.3.0
[tomato.git] / release / src / router / dropbear / cli-chansession.c
blob126c32f1689152fc3893296d7910833a6cc59f93
1 /*
2 * Dropbear SSH
3 *
4 * Copyright (c) 2002,2003 Matt Johnston
5 * Copyright (c) 2004 by Mihnea Stoenescu
6 * All rights reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE. */
26 #include "includes.h"
27 #include "packet.h"
28 #include "buffer.h"
29 #include "session.h"
30 #include "dbutil.h"
31 #include "channel.h"
32 #include "ssh.h"
33 #include "runopts.h"
34 #include "termcodes.h"
35 #include "chansession.h"
36 #include "agentfwd.h"
38 static void cli_closechansess(struct Channel *channel);
39 static int cli_initchansess(struct Channel *channel);
40 static void cli_chansessreq(struct Channel *channel);
42 static void send_chansess_pty_req(struct Channel *channel);
43 static void send_chansess_shell_req(struct Channel *channel);
45 static void cli_tty_setup();
47 const struct ChanType clichansess = {
48 0, /* sepfds */
49 "session", /* name */
50 cli_initchansess, /* inithandler */
51 NULL, /* checkclosehandler */
52 cli_chansessreq, /* reqhandler */
53 cli_closechansess, /* closehandler */
56 static void cli_chansessreq(struct Channel *channel) {
58 unsigned char* type = NULL;
59 int wantreply;
61 TRACE(("enter cli_chansessreq"))
63 type = buf_getstring(ses.payload, NULL);
64 wantreply = buf_getbool(ses.payload);
66 if (strcmp(type, "exit-status") == 0) {
67 cli_ses.retval = buf_getint(ses.payload);
68 TRACE(("got exit-status of '%d'", cli_ses.retval))
69 } else if (strcmp(type, "exit-signal") == 0) {
70 TRACE(("got exit-signal, ignoring it"))
71 } else {
72 TRACE(("unknown request '%s'", type))
73 send_msg_channel_failure(channel);
74 goto out;
77 out:
78 m_free(type);
82 /* If the main session goes, we close it up */
83 static void cli_closechansess(struct Channel *UNUSED(channel)) {
85 /* This channel hasn't gone yet, so we have > 1 */
86 if (ses.chancount > 1) {
87 dropbear_log(LOG_INFO, "Waiting for other channels to close...");
90 cli_tty_cleanup(); /* Restore tty modes etc */
94 void cli_start_send_channel_request(struct Channel *channel,
95 unsigned char *type) {
97 CHECKCLEARTOWRITE();
98 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
99 buf_putint(ses.writepayload, channel->remotechan);
101 buf_putstring(ses.writepayload, type, strlen(type));
105 /* Taken from OpenSSH's sshtty.c:
106 * RCSID("OpenBSD: sshtty.c,v 1.5 2003/09/19 17:43:35 markus Exp "); */
107 static void cli_tty_setup() {
109 struct termios tio;
111 TRACE(("enter cli_pty_setup"))
113 if (cli_ses.tty_raw_mode == 1) {
114 TRACE(("leave cli_tty_setup: already in raw mode!"))
115 return;
118 if (tcgetattr(STDIN_FILENO, &tio) == -1) {
119 dropbear_exit("Failed to set raw TTY mode");
122 /* make a copy */
123 cli_ses.saved_tio = tio;
125 tio.c_iflag |= IGNPAR;
126 tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF);
127 #ifdef IUCLC
128 tio.c_iflag &= ~IUCLC;
129 #endif
130 tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL);
131 #ifdef IEXTEN
132 tio.c_lflag &= ~IEXTEN;
133 #endif
134 tio.c_oflag &= ~OPOST;
135 tio.c_cc[VMIN] = 1;
136 tio.c_cc[VTIME] = 0;
137 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &tio) == -1) {
138 dropbear_exit("Failed to set raw TTY mode");
141 cli_ses.tty_raw_mode = 1;
142 TRACE(("leave cli_tty_setup"))
145 void cli_tty_cleanup() {
147 TRACE(("enter cli_tty_cleanup"))
149 if (cli_ses.tty_raw_mode == 0) {
150 TRACE(("leave cli_tty_cleanup: not in raw mode"))
151 return;
154 if (tcsetattr(STDIN_FILENO, TCSADRAIN, &cli_ses.saved_tio) == -1) {
155 dropbear_log(LOG_WARNING, "Failed restoring TTY");
156 } else {
157 cli_ses.tty_raw_mode = 0;
160 TRACE(("leave cli_tty_cleanup"))
163 static void put_termcodes() {
165 struct termios tio;
166 unsigned int sshcode;
167 const struct TermCode *termcode;
168 unsigned int value;
169 unsigned int mapcode;
171 unsigned int bufpos1, bufpos2;
173 TRACE(("enter put_termcodes"))
175 if (tcgetattr(STDIN_FILENO, &tio) == -1) {
176 dropbear_log(LOG_WARNING, "Failed reading termmodes");
177 buf_putint(ses.writepayload, 1); /* Just the terminator */
178 buf_putbyte(ses.writepayload, 0); /* TTY_OP_END */
179 return;
182 bufpos1 = ses.writepayload->pos;
183 buf_putint(ses.writepayload, 0); /* A placeholder for the final length */
185 /* As with Dropbear server, we ignore baud rates for now */
186 for (sshcode = 1; sshcode < MAX_TERMCODE; sshcode++) {
188 termcode = &termcodes[sshcode];
189 mapcode = termcode->mapcode;
191 switch (termcode->type) {
193 case TERMCODE_NONE:
194 continue;
196 case TERMCODE_CONTROLCHAR:
197 value = tio.c_cc[mapcode];
198 break;
200 case TERMCODE_INPUT:
201 value = tio.c_iflag & mapcode;
202 break;
204 case TERMCODE_OUTPUT:
205 value = tio.c_oflag & mapcode;
206 break;
208 case TERMCODE_LOCAL:
209 value = tio.c_lflag & mapcode;
210 break;
212 case TERMCODE_CONTROL:
213 value = tio.c_cflag & mapcode;
214 break;
216 default:
217 continue;
221 /* If we reach here, we have something to say */
222 buf_putbyte(ses.writepayload, sshcode);
223 buf_putint(ses.writepayload, value);
226 buf_putbyte(ses.writepayload, 0); /* THE END, aka TTY_OP_END */
228 /* Put the string length at the start of the buffer */
229 bufpos2 = ses.writepayload->pos;
231 buf_setpos(ses.writepayload, bufpos1); /* Jump back */
232 buf_putint(ses.writepayload, bufpos2 - bufpos1 - 4); /* len(termcodes) */
233 buf_setpos(ses.writepayload, bufpos2); /* Back where we were */
235 TRACE(("leave put_termcodes"))
238 static void put_winsize() {
240 struct winsize ws;
242 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) {
243 /* Some sane defaults */
244 ws.ws_row = 25;
245 ws.ws_col = 80;
246 ws.ws_xpixel = 0;
247 ws.ws_ypixel = 0;
250 buf_putint(ses.writepayload, ws.ws_col); /* Cols */
251 buf_putint(ses.writepayload, ws.ws_row); /* Rows */
252 buf_putint(ses.writepayload, ws.ws_xpixel); /* Width */
253 buf_putint(ses.writepayload, ws.ws_ypixel); /* Height */
257 static void sigwinch_handler(int UNUSED(unused)) {
259 cli_ses.winchange = 1;
263 void cli_chansess_winchange() {
265 unsigned int i;
266 struct Channel *channel = NULL;
268 for (i = 0; i < ses.chansize; i++) {
269 channel = ses.channels[i];
270 if (channel != NULL && channel->type == &clichansess) {
271 CHECKCLEARTOWRITE();
272 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_REQUEST);
273 buf_putint(ses.writepayload, channel->remotechan);
274 buf_putstring(ses.writepayload, "window-change", 13);
275 buf_putbyte(ses.writepayload, 0); /* FALSE says the spec */
276 put_winsize();
277 encrypt_packet();
280 cli_ses.winchange = 0;
283 static void send_chansess_pty_req(struct Channel *channel) {
285 unsigned char* term = NULL;
287 TRACE(("enter send_chansess_pty_req"))
289 cli_start_send_channel_request(channel, "pty-req");
291 /* Don't want replies */
292 buf_putbyte(ses.writepayload, 0);
294 /* Get the terminal */
295 term = getenv("TERM");
296 if (term == NULL) {
297 term = "vt100"; /* Seems a safe default */
299 buf_putstring(ses.writepayload, term, strlen(term));
301 /* Window size */
302 put_winsize();
304 /* Terminal mode encoding */
305 put_termcodes();
307 encrypt_packet();
309 /* Set up a window-change handler */
310 if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
311 dropbear_exit("Signal error");
313 TRACE(("leave send_chansess_pty_req"))
316 static void send_chansess_shell_req(struct Channel *channel) {
318 unsigned char* reqtype = NULL;
320 TRACE(("enter send_chansess_shell_req"))
322 if (cli_opts.cmd) {
323 if (cli_opts.is_subsystem) {
324 reqtype = "subsystem";
325 } else {
326 reqtype = "exec";
328 } else {
329 reqtype = "shell";
332 cli_start_send_channel_request(channel, reqtype);
334 /* XXX TODO */
335 buf_putbyte(ses.writepayload, 0); /* Don't want replies */
336 if (cli_opts.cmd) {
337 buf_putstring(ses.writepayload, cli_opts.cmd, strlen(cli_opts.cmd));
340 encrypt_packet();
341 TRACE(("leave send_chansess_shell_req"))
344 /* Shared for normal client channel and netcat-alike */
345 static int cli_init_stdpipe_sess(struct Channel *channel) {
346 channel->writefd = STDOUT_FILENO;
347 setnonblocking(STDOUT_FILENO);
349 channel->readfd = STDIN_FILENO;
350 setnonblocking(STDIN_FILENO);
352 channel->errfd = STDERR_FILENO;
353 setnonblocking(STDERR_FILENO);
355 channel->extrabuf = cbuf_new(opts.recv_window);
356 return 0;
359 static int cli_initchansess(struct Channel *channel) {
361 cli_init_stdpipe_sess(channel);
363 #ifdef ENABLE_CLI_AGENTFWD
364 if (cli_opts.agent_fwd) {
365 cli_setup_agent(channel);
367 #endif
369 if (cli_opts.wantpty) {
370 send_chansess_pty_req(channel);
373 send_chansess_shell_req(channel);
375 if (cli_opts.wantpty) {
376 cli_tty_setup();
379 return 0; /* Success */
382 #ifdef ENABLE_CLI_NETCAT
384 static const struct ChanType cli_chan_netcat = {
385 0, /* sepfds */
386 "direct-tcpip",
387 cli_init_stdpipe_sess, /* inithandler */
388 NULL,
389 NULL,
390 cli_closechansess
393 void cli_send_netcat_request() {
395 const unsigned char* source_host = "127.0.0.1";
396 const int source_port = 22;
398 cli_opts.wantpty = 0;
400 if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat)
401 == DROPBEAR_FAILURE) {
402 dropbear_exit("Couldn't open initial channel");
405 buf_putstring(ses.writepayload, cli_opts.netcat_host,
406 strlen(cli_opts.netcat_host));
407 buf_putint(ses.writepayload, cli_opts.netcat_port);
409 /* originator ip - localhost is accurate enough */
410 buf_putstring(ses.writepayload, source_host, strlen(source_host));
411 buf_putint(ses.writepayload, source_port);
413 encrypt_packet();
414 TRACE(("leave cli_send_chansess_request"))
416 #endif
418 void cli_send_chansess_request() {
420 TRACE(("enter cli_send_chansess_request"))
422 if (send_msg_channel_open_init(STDIN_FILENO, &clichansess)
423 == DROPBEAR_FAILURE) {
424 dropbear_exit("Couldn't open initial channel");
427 /* No special channel request data */
428 encrypt_packet();
429 TRACE(("leave cli_send_chansess_request"))