dropbar: Update to version 2013.60.1.
[tomato.git] / release / src / router / dropbear / cli-session.c
blob401c9e23b7cf64f26decbb991b6274b6ab6fa088
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 "random.h"
35 #include "service.h"
36 #include "runopts.h"
37 #include "chansession.h"
38 #include "agentfwd.h"
40 static void cli_remoteclosed();
41 static void cli_sessionloop();
42 static void cli_session_init();
43 static void cli_finished();
44 static void recv_msg_service_accept(void);
45 static void cli_session_cleanup(void);
47 struct clientsession cli_ses; /* GLOBAL */
49 /* Sorted in decreasing frequency will be more efficient - data and window
50 * should be first */
51 static const packettype cli_packettypes[] = {
52 /* TYPE, FUNCTION */
53 {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
54 {SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data},
55 {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
56 {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */
57 {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */
58 {SSH_MSG_KEXINIT, recv_msg_kexinit},
59 {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
60 {SSH_MSG_NEWKEYS, recv_msg_newkeys},
61 {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
62 {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
63 {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
64 {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
65 {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
66 {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
67 {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
68 {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
69 {SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
70 #ifdef ENABLE_CLI_REMOTETCPFWD
71 {SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
72 {SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
73 #endif
74 {0, 0} /* End */
77 static const struct ChanType *cli_chantypes[] = {
78 #ifdef ENABLE_CLI_REMOTETCPFWD
79 &cli_chan_tcpremote,
80 #endif
81 #ifdef ENABLE_CLI_AGENTFWD
82 &cli_chan_agent,
83 #endif
84 NULL /* Null termination */
87 void cli_session(int sock_in, int sock_out) {
89 seedrandom();
91 crypto_init();
93 common_session_init(sock_in, sock_out);
95 chaninitialise(cli_chantypes);
97 /* Set up cli_ses vars */
98 cli_session_init();
100 /* Ready to go */
101 sessinitdone = 1;
103 /* Exchange identification */
104 send_session_identification();
106 send_msg_kexinit();
108 session_loop(cli_sessionloop);
110 /* Not reached */
114 #ifdef USE_KEX_FIRST_FOLLOWS
115 static void cli_send_kex_first_guess() {
116 send_msg_kexdh_init();
118 #endif
120 static void cli_session_init() {
122 cli_ses.state = STATE_NOTHING;
123 cli_ses.kex_state = KEX_NOTHING;
125 cli_ses.tty_raw_mode = 0;
126 cli_ses.winchange = 0;
128 /* We store std{in,out,err}'s flags, so we can set them back on exit
129 * (otherwise busybox's ash isn't happy */
130 cli_ses.stdincopy = dup(STDIN_FILENO);
131 cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
132 cli_ses.stdoutcopy = dup(STDOUT_FILENO);
133 cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
134 cli_ses.stderrcopy = dup(STDERR_FILENO);
135 cli_ses.stderrflags = fcntl(STDERR_FILENO, F_GETFL, 0);
137 cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
138 specific exit status */
140 /* Auth */
141 cli_ses.lastprivkey = NULL;
142 cli_ses.lastauthtype = 0;
144 #ifdef DROPBEAR_NONE_CIPHER
145 cli_ses.cipher_none_after_auth = get_algo_usable(sshciphers, "none");
146 set_algo_usable(sshciphers, "none", 0);
147 #else
148 cli_ses.cipher_none_after_auth = 0;
149 #endif
151 /* For printing "remote host closed" for the user */
152 ses.remoteclosed = cli_remoteclosed;
154 ses.extra_session_cleanup = cli_session_cleanup;
156 /* packet handlers */
157 ses.packettypes = cli_packettypes;
159 ses.isserver = 0;
161 #ifdef USE_KEX_FIRST_FOLLOWS
162 ses.send_kex_first_guess = cli_send_kex_first_guess;
163 #endif
167 static void send_msg_service_request(char* servicename) {
169 TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
171 CHECKCLEARTOWRITE();
173 buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
174 buf_putstring(ses.writepayload, servicename, strlen(servicename));
176 encrypt_packet();
177 TRACE(("leave send_msg_service_request"))
180 static void recv_msg_service_accept(void) {
181 // do nothing, if it failed then the server MUST have disconnected
184 /* This function drives the progress of the session - it initiates KEX,
185 * service, userauth and channel requests */
186 static void cli_sessionloop() {
188 TRACE2(("enter cli_sessionloop"))
190 if (ses.lastpacket == 0) {
191 TRACE2(("exit cli_sessionloop: no real packets yet"))
192 return;
195 if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
196 /* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
197 * negotiation would have failed. */
198 if (!ses.kexstate.our_first_follows_matches) {
199 send_msg_kexdh_init();
201 cli_ses.kex_state = KEXDH_INIT_SENT;
202 TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
203 return;
206 /* A KEX has finished, so we should go back to our KEX_NOTHING state */
207 if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.sentnewkeys) {
208 cli_ses.kex_state = KEX_NOTHING;
211 /* We shouldn't do anything else if a KEX is in progress */
212 if (cli_ses.kex_state != KEX_NOTHING) {
213 TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"))
214 return;
217 if (ses.kexstate.donefirstkex == 0) {
218 /* We might reach here if we have partial packet reads or have
219 * received SSG_MSG_IGNORE etc. Just skip it */
220 TRACE2(("donefirstkex false\n"))
221 return;
224 switch (cli_ses.state) {
226 case STATE_NOTHING:
227 /* We've got the transport layer sorted, we now need to request
228 * userauth */
229 send_msg_service_request(SSH_SERVICE_USERAUTH);
230 cli_auth_getmethods();
231 cli_ses.state = USERAUTH_REQ_SENT;
232 TRACE(("leave cli_sessionloop: sent userauth methods req"))
233 return;
235 case USERAUTH_FAIL_RCVD:
236 if (cli_auth_try() == DROPBEAR_FAILURE) {
237 dropbear_exit("No auth methods could be used.");
239 cli_ses.state = USERAUTH_REQ_SENT;
240 TRACE(("leave cli_sessionloop: cli_auth_try"))
241 return;
243 case USERAUTH_SUCCESS_RCVD:
245 #ifdef DROPBEAR_NONE_CIPHER
246 if (cli_ses.cipher_none_after_auth)
248 set_algo_usable(sshciphers, "none", 1);
249 send_msg_kexinit();
251 #endif
253 if (cli_opts.backgrounded) {
254 int devnull;
255 /* keeping stdin open steals input from the terminal and
256 is confusing, though stdout/stderr could be useful. */
257 devnull = open(_PATH_DEVNULL, O_RDONLY);
258 if (devnull < 0) {
259 dropbear_exit("Opening /dev/null: %d %s",
260 errno, strerror(errno));
262 dup2(devnull, STDIN_FILENO);
263 if (daemon(0, 1) < 0) {
264 dropbear_exit("Backgrounding failed: %d %s",
265 errno, strerror(errno));
269 #ifdef ENABLE_CLI_NETCAT
270 if (cli_opts.netcat_host) {
271 cli_send_netcat_request();
272 } else
273 #endif
274 if (!cli_opts.no_cmd) {
275 cli_send_chansess_request();
278 #ifdef ENABLE_CLI_LOCALTCPFWD
279 setup_localtcp();
280 #endif
281 #ifdef ENABLE_CLI_REMOTETCPFWD
282 setup_remotetcp();
283 #endif
285 TRACE(("leave cli_sessionloop: running"))
286 cli_ses.state = SESSION_RUNNING;
287 return;
289 case SESSION_RUNNING:
290 if (ses.chancount < 1 && !cli_opts.no_cmd) {
291 cli_finished();
294 if (cli_ses.winchange) {
295 cli_chansess_winchange();
297 return;
299 /* XXX more here needed */
302 default:
303 break;
306 TRACE2(("leave cli_sessionloop: fell out"))
310 static void cli_session_cleanup(void) {
312 if (!sessinitdone) {
313 return;
316 /* Set std{in,out,err} back to non-blocking - busybox ash dies nastily if
317 * we don't revert the flags */
318 fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
319 fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
320 fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
322 cli_tty_cleanup();
326 static void cli_finished() {
328 session_cleanup();
329 fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
330 cli_opts.remotehost, cli_opts.remoteport);
331 exit(cli_ses.retval);
335 /* called when the remote side closes the connection */
336 static void cli_remoteclosed() {
338 /* XXX TODO perhaps print a friendlier message if we get this but have
339 * already sent/received disconnect message(s) ??? */
340 m_close(ses.sock_in);
341 m_close(ses.sock_out);
342 ses.sock_in = -1;
343 ses.sock_out = -1;
344 dropbear_exit("Remote closed the connection");
347 /* Operates in-place turning dirty (untrusted potentially containing control
348 * characters) text into clean text.
349 * Note: this is safe only with ascii - other charsets could have problems. */
350 void cleantext(unsigned char* dirtytext) {
352 unsigned int i, j;
353 unsigned char c;
355 j = 0;
356 for (i = 0; dirtytext[i] != '\0'; i++) {
358 c = dirtytext[i];
359 /* We can ignore '\r's */
360 if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
361 dirtytext[j] = c;
362 j++;
365 /* Null terminate */
366 dirtytext[j] = '\0';