cli-session.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  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:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  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. */
  25. #include "includes.h"
  26. #include "session.h"
  27. #include "dbutil.h"
  28. #include "kex.h"
  29. #include "ssh.h"
  30. #include "packet.h"
  31. #include "tcpfwd.h"
  32. #include "channel.h"
  33. #include "dbrandom.h"
  34. #include "service.h"
  35. #include "runopts.h"
  36. #include "chansession.h"
  37. #include "agentfwd.h"
  38. #include "crypto_desc.h"
  39. #include "netio.h"
  40. static void cli_remoteclosed(void) ATTRIB_NORETURN;
  41. static void cli_sessionloop(void);
  42. static void cli_session_init(pid_t proxy_cmd_pid);
  43. static void cli_finished(void) ATTRIB_NORETURN;
  44. static void recv_msg_service_accept(void);
  45. static void cli_session_cleanup(void);
  46. static void recv_msg_global_request_cli(void);
  47. struct clientsession cli_ses; /* GLOBAL */
  48. /* Sorted in decreasing frequency will be more efficient - data and window
  49. * should be first */
  50. static const packettype cli_packettypes[] = {
  51. /* TYPE, FUNCTION */
  52. {SSH_MSG_CHANNEL_DATA, recv_msg_channel_data},
  53. {SSH_MSG_CHANNEL_EXTENDED_DATA, recv_msg_channel_extended_data},
  54. {SSH_MSG_CHANNEL_WINDOW_ADJUST, recv_msg_channel_window_adjust},
  55. {SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure}, /* client */
  56. {SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success}, /* client */
  57. {SSH_MSG_KEXINIT, recv_msg_kexinit},
  58. {SSH_MSG_KEXDH_REPLY, recv_msg_kexdh_reply}, /* client */
  59. {SSH_MSG_NEWKEYS, recv_msg_newkeys},
  60. {SSH_MSG_SERVICE_ACCEPT, recv_msg_service_accept}, /* client */
  61. {SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
  62. {SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
  63. {SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
  64. {SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
  65. {SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
  66. {SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
  67. {SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
  68. {SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
  69. {SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_cli},
  70. {SSH_MSG_CHANNEL_SUCCESS, ignore_recv_response},
  71. {SSH_MSG_CHANNEL_FAILURE, ignore_recv_response},
  72. #ifdef ENABLE_CLI_REMOTETCPFWD
  73. {SSH_MSG_REQUEST_SUCCESS, cli_recv_msg_request_success}, /* client */
  74. {SSH_MSG_REQUEST_FAILURE, cli_recv_msg_request_failure}, /* client */
  75. #else
  76. /* For keepalive */
  77. {SSH_MSG_REQUEST_SUCCESS, ignore_recv_response},
  78. {SSH_MSG_REQUEST_FAILURE, ignore_recv_response},
  79. #endif
  80. {0, 0} /* End */
  81. };
  82. static const struct ChanType *cli_chantypes[] = {
  83. #ifdef ENABLE_CLI_REMOTETCPFWD
  84. &cli_chan_tcpremote,
  85. #endif
  86. #ifdef ENABLE_CLI_AGENTFWD
  87. &cli_chan_agent,
  88. #endif
  89. NULL /* Null termination */
  90. };
  91. void cli_connected(int result, int sock, void* userdata, const char *errstring)
  92. {
  93. struct sshsession *myses = userdata;
  94. if (result == DROPBEAR_FAILURE) {
  95. dropbear_exit("Connect failed: %s", errstring);
  96. }
  97. myses->sock_in = myses->sock_out = sock;
  98. update_channel_prio();
  99. }
  100. void cli_session(int sock_in, int sock_out, struct dropbear_progress_connection *progress, pid_t proxy_cmd_pid) {
  101. common_session_init(sock_in, sock_out);
  102. if (progress) {
  103. connect_set_writequeue(progress, &ses.writequeue);
  104. }
  105. chaninitialise(cli_chantypes);
  106. /* Set up cli_ses vars */
  107. cli_session_init(proxy_cmd_pid);
  108. /* Ready to go */
  109. sessinitdone = 1;
  110. /* Exchange identification */
  111. send_session_identification();
  112. kexfirstinitialise(); /* initialise the kex state */
  113. send_msg_kexinit();
  114. session_loop(cli_sessionloop);
  115. /* Not reached */
  116. }
  117. #ifdef USE_KEX_FIRST_FOLLOWS
  118. static void cli_send_kex_first_guess() {
  119. send_msg_kexdh_init();
  120. }
  121. #endif
  122. static void cli_session_init(pid_t proxy_cmd_pid) {
  123. cli_ses.state = STATE_NOTHING;
  124. cli_ses.kex_state = KEX_NOTHING;
  125. cli_ses.tty_raw_mode = 0;
  126. cli_ses.winchange = 0;
  127. /* We store std{in,out,err}'s flags, so we can set them back on exit
  128. * (otherwise busybox's ash isn't happy */
  129. cli_ses.stdincopy = dup(STDIN_FILENO);
  130. cli_ses.stdinflags = fcntl(STDIN_FILENO, F_GETFL, 0);
  131. cli_ses.stdoutcopy = dup(STDOUT_FILENO);
  132. cli_ses.stdoutflags = fcntl(STDOUT_FILENO, F_GETFL, 0);
  133. cli_ses.stderrcopy = dup(STDERR_FILENO);
  134. cli_ses.stderrflags = fcntl(STDERR_FILENO, F_GETFL, 0);
  135. cli_ses.retval = EXIT_SUCCESS; /* Assume it's clean if we don't get a
  136. specific exit status */
  137. cli_ses.proxy_cmd_pid = proxy_cmd_pid;
  138. TRACE(("proxy command PID='%d'", proxy_cmd_pid));
  139. /* Auth */
  140. cli_ses.lastprivkey = NULL;
  141. cli_ses.lastauthtype = 0;
  142. #ifdef DROPBEAR_NONE_CIPHER
  143. cli_ses.cipher_none_after_auth = get_algo_usable(sshciphers, "none");
  144. set_algo_usable(sshciphers, "none", 0);
  145. #else
  146. cli_ses.cipher_none_after_auth = 0;
  147. #endif
  148. /* For printing "remote host closed" for the user */
  149. ses.remoteclosed = cli_remoteclosed;
  150. ses.extra_session_cleanup = cli_session_cleanup;
  151. /* packet handlers */
  152. ses.packettypes = cli_packettypes;
  153. ses.isserver = 0;
  154. #ifdef USE_KEX_FIRST_FOLLOWS
  155. ses.send_kex_first_guess = cli_send_kex_first_guess;
  156. #endif
  157. }
  158. static void send_msg_service_request(char* servicename) {
  159. TRACE(("enter send_msg_service_request: servicename='%s'", servicename))
  160. CHECKCLEARTOWRITE();
  161. buf_putbyte(ses.writepayload, SSH_MSG_SERVICE_REQUEST);
  162. buf_putstring(ses.writepayload, servicename, strlen(servicename));
  163. encrypt_packet();
  164. TRACE(("leave send_msg_service_request"))
  165. }
  166. static void recv_msg_service_accept(void) {
  167. /* do nothing, if it failed then the server MUST have disconnected */
  168. }
  169. /* This function drives the progress of the session - it initiates KEX,
  170. * service, userauth and channel requests */
  171. static void cli_sessionloop() {
  172. TRACE2(("enter cli_sessionloop"))
  173. if (ses.lastpacket == 0) {
  174. TRACE2(("exit cli_sessionloop: no real packets yet"))
  175. return;
  176. }
  177. if (ses.lastpacket == SSH_MSG_KEXINIT && cli_ses.kex_state == KEX_NOTHING) {
  178. /* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
  179. * negotiation would have failed. */
  180. if (!ses.kexstate.our_first_follows_matches) {
  181. send_msg_kexdh_init();
  182. }
  183. cli_ses.kex_state = KEXDH_INIT_SENT;
  184. TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"))
  185. return;
  186. }
  187. /* A KEX has finished, so we should go back to our KEX_NOTHING state */
  188. if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.sentnewkeys) {
  189. cli_ses.kex_state = KEX_NOTHING;
  190. }
  191. /* We shouldn't do anything else if a KEX is in progress */
  192. if (cli_ses.kex_state != KEX_NOTHING) {
  193. TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"))
  194. return;
  195. }
  196. if (ses.kexstate.donefirstkex == 0) {
  197. /* We might reach here if we have partial packet reads or have
  198. * received SSG_MSG_IGNORE etc. Just skip it */
  199. TRACE2(("donefirstkex false\n"))
  200. return;
  201. }
  202. switch (cli_ses.state) {
  203. case STATE_NOTHING:
  204. /* We've got the transport layer sorted, we now need to request
  205. * userauth */
  206. send_msg_service_request(SSH_SERVICE_USERAUTH);
  207. cli_auth_getmethods();
  208. cli_ses.state = USERAUTH_REQ_SENT;
  209. TRACE(("leave cli_sessionloop: sent userauth methods req"))
  210. return;
  211. case USERAUTH_REQ_SENT:
  212. TRACE(("leave cli_sessionloop: waiting, req_sent"))
  213. return;
  214. case USERAUTH_FAIL_RCVD:
  215. if (cli_auth_try() == DROPBEAR_FAILURE) {
  216. dropbear_exit("No auth methods could be used.");
  217. }
  218. cli_ses.state = USERAUTH_REQ_SENT;
  219. TRACE(("leave cli_sessionloop: cli_auth_try"))
  220. return;
  221. case USERAUTH_SUCCESS_RCVD:
  222. #ifndef DISABLE_SYSLOG
  223. if (opts.usingsyslog) {
  224. dropbear_log(LOG_INFO, "Authentication succeeded.");
  225. }
  226. #endif
  227. #ifdef DROPBEAR_NONE_CIPHER
  228. if (cli_ses.cipher_none_after_auth)
  229. {
  230. set_algo_usable(sshciphers, "none", 1);
  231. send_msg_kexinit();
  232. }
  233. #endif
  234. if (cli_opts.backgrounded) {
  235. int devnull;
  236. /* keeping stdin open steals input from the terminal and
  237. is confusing, though stdout/stderr could be useful. */
  238. devnull = open(_PATH_DEVNULL, O_RDONLY);
  239. if (devnull < 0) {
  240. dropbear_exit("Opening /dev/null: %d %s",
  241. errno, strerror(errno));
  242. }
  243. dup2(devnull, STDIN_FILENO);
  244. if (daemon(0, 1) < 0) {
  245. dropbear_exit("Backgrounding failed: %d %s",
  246. errno, strerror(errno));
  247. }
  248. }
  249. #ifdef ENABLE_CLI_NETCAT
  250. if (cli_opts.netcat_host) {
  251. cli_send_netcat_request();
  252. } else
  253. #endif
  254. if (!cli_opts.no_cmd) {
  255. cli_send_chansess_request();
  256. }
  257. #ifdef ENABLE_CLI_LOCALTCPFWD
  258. setup_localtcp();
  259. #endif
  260. #ifdef ENABLE_CLI_REMOTETCPFWD
  261. setup_remotetcp();
  262. #endif
  263. TRACE(("leave cli_sessionloop: running"))
  264. cli_ses.state = SESSION_RUNNING;
  265. return;
  266. case SESSION_RUNNING:
  267. if (ses.chancount < 1 && !cli_opts.no_cmd) {
  268. cli_finished();
  269. }
  270. if (cli_ses.winchange) {
  271. cli_chansess_winchange();
  272. }
  273. return;
  274. /* XXX more here needed */
  275. default:
  276. break;
  277. }
  278. TRACE2(("leave cli_sessionloop: fell out"))
  279. }
  280. void kill_proxy_command(void) {
  281. /*
  282. * Send SIGHUP to proxy command if used. We don't wait() in
  283. * case it hangs and instead rely on init to reap the child
  284. */
  285. if (cli_ses.proxy_cmd_pid > 1) {
  286. TRACE(("killing proxy command with PID='%d'", cli_ses.proxy_cmd_pid));
  287. kill(cli_ses.proxy_cmd_pid, SIGHUP);
  288. }
  289. }
  290. static void cli_session_cleanup(void) {
  291. if (!sessinitdone) {
  292. return;
  293. }
  294. kill_proxy_command();
  295. /* Set std{in,out,err} back to non-blocking - busybox ash dies nastily if
  296. * we don't revert the flags */
  297. /* Ignore return value since there's nothing we can do */
  298. (void)fcntl(cli_ses.stdincopy, F_SETFL, cli_ses.stdinflags);
  299. (void)fcntl(cli_ses.stdoutcopy, F_SETFL, cli_ses.stdoutflags);
  300. (void)fcntl(cli_ses.stderrcopy, F_SETFL, cli_ses.stderrflags);
  301. cli_tty_cleanup();
  302. }
  303. static void cli_finished() {
  304. session_cleanup();
  305. fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
  306. cli_opts.remotehost, cli_opts.remoteport);
  307. exit(cli_ses.retval);
  308. }
  309. /* called when the remote side closes the connection */
  310. static void cli_remoteclosed() {
  311. /* XXX TODO perhaps print a friendlier message if we get this but have
  312. * already sent/received disconnect message(s) ??? */
  313. m_close(ses.sock_in);
  314. m_close(ses.sock_out);
  315. ses.sock_in = -1;
  316. ses.sock_out = -1;
  317. dropbear_exit("Remote closed the connection");
  318. }
  319. /* Operates in-place turning dirty (untrusted potentially containing control
  320. * characters) text into clean text.
  321. * Note: this is safe only with ascii - other charsets could have problems. */
  322. void cleantext(char* dirtytext) {
  323. unsigned int i, j;
  324. char c;
  325. j = 0;
  326. for (i = 0; dirtytext[i] != '\0'; i++) {
  327. c = dirtytext[i];
  328. /* We can ignore '\r's */
  329. if ( (c >= ' ' && c <= '~') || c == '\n' || c == '\t') {
  330. dirtytext[j] = c;
  331. j++;
  332. }
  333. }
  334. /* Null terminate */
  335. dirtytext[j] = '\0';
  336. }
  337. static void recv_msg_global_request_cli(void) {
  338. TRACE(("recv_msg_global_request_cli"))
  339. /* Send a proper rejection */
  340. send_msg_request_failure();
  341. }