dropbear: update to 2015.67
[tomato.git] / release / src / router / dropbear / cli-session.c
bloba484bf731ae872f4e0d1bde0280c012ea4b75120
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 "session.h"
28 #include "dbutil.h"
29 #include "kex.h"
30 #include "ssh.h"
31 #include "packet.h"
32 #include "tcpfwd.h"
33 #include "channel.h"
34 #include "dbrandom.h"
35 #include "service.h"
36 #include "runopts.h"
37 #include "chansession.h"
38 #include "agentfwd.h"
39 #include "crypto_desc.h"
41 static void cli_remoteclosed();
42 static void cli_sessionloop();
43 static void cli_session_init();
44 static void cli_finished();
45 static void recv_msg_service_accept(void);
46 static void cli_session_cleanup(void);
47 static void recv_msg_global_request_cli(void);
49 struct clientsession cli_ses; /* GLOBAL */
51 /* Sorted in decreasing frequency will be more efficient - data and window
52 * should be first */
53 static const packettype cli_packettypes[] = {
54 /* TYPE, FUNCTION */
55 {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
56 {SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data},
57 {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
58 {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */
59 {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */
60 {SSH_MSG_KEXINIT, recv_msg_kexinit},
61 {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
62 {SSH_MSG_NEWKEYS, recv_msg_newkeys},
63 {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
64 {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
65 {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
66 {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
67 {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
68 {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
69 {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
70 {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
71 {SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
72 {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli},
73 {SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response},
74 {SSH_MSG_CHANNEL_FAILURE, ignore_recv_response},
75 #ifdef ENABLE_CLI_REMOTETCPFWD
76 {SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
77 {SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
78 #else
79 /* For keepalive */
80 {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
81 {SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
82 #endif
83 {0, 0} /* End */
86 static const struct ChanType *cli_chantypes[] = {
87 #ifdef ENABLE_CLI_REMOTETCPFWD
88 &cli_chan_tcpremote,
89 #endif
90 #ifdef ENABLE_CLI_AGENTFWD
91 &cli_chan_agent,
92 #endif
93 NULL /* Null termination */
96 void cli_session(int sock_in, int sock_out) {
98 common_session_init(sock_in, sock_out);
100 chaninitialise(cli_chantypes);
102 /* Set up cli_ses vars */
103 cli_session_init();
105 /* Ready to go */
106 sessinitdone = 1;
108 /* Exchange identification */
109 send_session_identification();
111 send_msg_kexinit();
113 session_loop(cli_sessionloop);
115 /* Not reached */
119 #ifdef USE_KEX_FIRST_FOLLOWS
120 static void cli_send_kex_first_guess() {
121 send_msg_kexdh_init();
123 #endif
125 static void cli_session_init() {
127 cli_ses.state = STATE_NOTHING;
128 cli_ses.kex_state = KEX_NOTHING;
130 cli_ses.tty_raw_mode = 0;
131 cli_ses.winchange = 0;
133 /* We store std{in,out,err}'s flags, so we can set them back on exit
134 * (otherwise busybox's ash isn't happy */
135 cli_ses.stdincopy = dup(STDIN_FILENO);
136 cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
137 cli_ses.stdoutcopy = dup(STDOUT_FILENO);
138 cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
139 cli_ses.stderrcopy = dup(STDERR_FILENO);
140 cli_ses.stderrflags = fcntl(STDERR_FILENO, F_GETFL, 0);
142 cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
143 specific exit status */
145 /* Auth */
146 cli_ses.lastprivkey = NULL;
147 cli_ses.lastauthtype = 0;
149 #ifdef DROPBEAR_NONE_CIPHER
150 cli_ses.cipher_none_after_auth = get_algo_usable(sshciphers, "none");
151 set_algo_usable(sshciphers, "none", 0);
152 #else
153 cli_ses.cipher_none_after_auth = 0;
154 #endif
156 /* For printing "remote host closed" for the user */
157 ses.remoteclosed = cli_remoteclosed;
159 ses.extra_session_cleanup = cli_session_cleanup;
161 /* packet handlers */
162 ses.packettypes = cli_packettypes;
164 ses.isserver = 0;
166 #ifdef USE_KEX_FIRST_FOLLOWS
167 ses.send_kex_first_guess = cli_send_kex_first_guess;
168 #endif
172 static void send_msg_service_request(char* servicename) {
174 TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
176 CHECKCLEARTOWRITE();
178 buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
179 buf_putstring(ses.writepayload, servicename, strlen(servicename));
181 encrypt_packet();
182 TRACE(("leave send_msg_service_request"))
185 static void recv_msg_service_accept(void) {
186 /* do nothing, if it failed then the server MUST have disconnected */
189 /* This function drives the progress of the session - it initiates KEX,
190 * service, userauth and channel requests */
191 static void cli_sessionloop() {
193 TRACE2(("enter cli_sessionloop"))
195 if (ses.lastpacket == 0) {
196 TRACE2(("exit cli_sessionloop: no real packets yet"))
197 return;
200 if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
201 /* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
202 * negotiation would have failed. */
203 if (!ses.kexstate.our_first_follows_matches) {
204 send_msg_kexdh_init();
206 cli_ses.kex_state = KEXDH_INIT_SENT;
207 TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
208 return;
211 /* A KEX has finished, so we should go back to our KEX_NOTHING state */
212 if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.sentnewkeys) {
213 cli_ses.kex_state = KEX_NOTHING;
216 /* We shouldn't do anything else if a KEX is in progress */
217 if (cli_ses.kex_state != KEX_NOTHING) {
218 TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"))
219 return;
222 if (ses.kexstate.donefirstkex == 0) {
223 /* We might reach here if we have partial packet reads or have
224 * received SSG_MSG_IGNORE etc. Just skip it */
225 TRACE2(("donefirstkex false\n"))
226 return;
229 switch (cli_ses.state) {
231 case STATE_NOTHING:
232 /* We've got the transport layer sorted, we now need to request
233 * userauth */
234 send_msg_service_request(SSH_SERVICE_USERAUTH);
235 cli_auth_getmethods();
236 cli_ses.state = USERAUTH_REQ_SENT;
237 TRACE(("leave cli_sessionloop: sent userauth methods req"))
238 return;
240 case USERAUTH_REQ_SENT:
241 TRACE(("leave cli_sessionloop: waiting, req_sent"))
242 return;
244 case USERAUTH_FAIL_RCVD:
245 if (cli_auth_try() == DROPBEAR_FAILURE) {
246 dropbear_exit("No auth methods could be used.");
248 cli_ses.state = USERAUTH_REQ_SENT;
249 TRACE(("leave cli_sessionloop: cli_auth_try"))
250 return;
252 case USERAUTH_SUCCESS_RCVD:
254 #ifdef DROPBEAR_NONE_CIPHER
255 if (cli_ses.cipher_none_after_auth)
257 set_algo_usable(sshciphers, "none", 1);
258 send_msg_kexinit();
260 #endif
262 if (cli_opts.backgrounded) {
263 int devnull;
264 /* keeping stdin open steals input from the terminal and
265 is confusing, though stdout/stderr could be useful. */
266 devnull = open(_PATH_DEVNULL, O_RDONLY);
267 if (devnull < 0) {
268 dropbear_exit("Opening /dev/null: %d %s",
269 errno, strerror(errno));
271 dup2(devnull, STDIN_FILENO);
272 if (daemon(0, 1) < 0) {
273 dropbear_exit("Backgrounding failed: %d %s",
274 errno, strerror(errno));
278 #ifdef ENABLE_CLI_NETCAT
279 if (cli_opts.netcat_host) {
280 cli_send_netcat_request();
281 } else
282 #endif
283 if (!cli_opts.no_cmd) {
284 cli_send_chansess_request();
287 #ifdef ENABLE_CLI_LOCALTCPFWD
288 setup_localtcp();
289 #endif
290 #ifdef ENABLE_CLI_REMOTETCPFWD
291 setup_remotetcp();
292 #endif
294 TRACE(("leave cli_sessionloop: running"))
295 cli_ses.state = SESSION_RUNNING;
296 return;
298 case SESSION_RUNNING:
299 if (ses.chancount < 1 && !cli_opts.no_cmd) {
300 cli_finished();
303 if (cli_ses.winchange) {
304 cli_chansess_winchange();
306 return;
308 /* XXX more here needed */
311 default:
312 break;
315 TRACE2(("leave cli_sessionloop: fell out"))
319 static void cli_session_cleanup(void) {
321 if (!sessinitdone) {
322 return;
325 /* Set std{in,out,err} back to non-blocking - busybox ash dies nastily if
326 * we don't revert the flags */
327 fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
328 fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
329 fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
331 cli_tty_cleanup();
335 static void cli_finished() {
337 session_cleanup();
338 fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
339 cli_opts.remotehost, cli_opts.remoteport);
340 exit(cli_ses.retval);
344 /* called when the remote side closes the connection */
345 static void cli_remoteclosed() {
347 /* XXX TODO perhaps print a friendlier message if we get this but have
348 * already sent/received disconnect message(s) ??? */
349 m_close(ses.sock_in);
350 m_close(ses.sock_out);
351 ses.sock_in = -1;
352 ses.sock_out = -1;
353 dropbear_exit("Remote closed the connection");
356 /* Operates in-place turning dirty (untrusted potentially containing control
357 * characters) text into clean text.
358 * Note: this is safe only with ascii - other charsets could have problems. */
359 void cleantext(unsigned char* dirtytext) {
361 unsigned int i, j;
362 unsigned char c;
364 j = 0;
365 for (i = 0; dirtytext[i] != '\0'; i++) {
367 c = dirtytext[i];
368 /* We can ignore '\r's */
369 if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
370 dirtytext[j] = c;
371 j++;
374 /* Null terminate */
375 dirtytext[j] = '\0';
378 static void recv_msg_global_request_cli(void) {
379 TRACE(("recv_msg_global_request_cli"))
380 /* Send a proper rejection */
381 send_msg_request_failure();