cli-tcpfwd.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /*
  2. * Dropbear SSH
  3. *
  4. * Copyright (c) 2002,2003 Matt Johnston
  5. * All rights reserved.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  23. * SOFTWARE. */
  24. #include "includes.h"
  25. #include "dbutil.h"
  26. #include "tcpfwd.h"
  27. #include "channel.h"
  28. #include "runopts.h"
  29. #include "session.h"
  30. #include "ssh.h"
  31. #include "netio.h"
  32. #if DROPBEAR_CLI_REMOTETCPFWD
  33. static int newtcpforwarded(struct Channel * channel);
  34. const struct ChanType cli_chan_tcpremote = {
  35. "forwarded-tcpip",
  36. newtcpforwarded,
  37. NULL,
  38. NULL,
  39. NULL,
  40. NULL
  41. };
  42. #endif
  43. #if DROPBEAR_CLI_LOCALTCPFWD
  44. static int cli_localtcp(const char* listenaddr,
  45. unsigned int listenport,
  46. const char* remoteaddr,
  47. unsigned int remoteport);
  48. static const struct ChanType cli_chan_tcplocal = {
  49. "direct-tcpip",
  50. NULL,
  51. NULL,
  52. NULL,
  53. NULL,
  54. NULL
  55. };
  56. #endif
  57. #if DROPBEAR_CLI_ANYTCPFWD
  58. static void fwd_failed(const char* format, ...) ATTRIB_PRINTF(1,2);
  59. static void fwd_failed(const char* format, ...)
  60. {
  61. va_list param;
  62. va_start(param, format);
  63. if (cli_opts.exit_on_fwd_failure) {
  64. _dropbear_exit(EXIT_FAILURE, format, param);
  65. } else {
  66. _dropbear_log(LOG_WARNING, format, param);
  67. }
  68. va_end(param);
  69. }
  70. #endif
  71. #if DROPBEAR_CLI_LOCALTCPFWD
  72. void setup_localtcp() {
  73. m_list_elem *iter;
  74. int ret;
  75. TRACE(("enter setup_localtcp"))
  76. for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
  77. struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
  78. ret = cli_localtcp(
  79. fwd->listenaddr,
  80. fwd->listenport,
  81. fwd->connectaddr,
  82. fwd->connectport);
  83. if (ret == DROPBEAR_FAILURE) {
  84. fwd_failed("Failed local port forward %s:%d:%s:%d",
  85. fwd->listenaddr,
  86. fwd->listenport,
  87. fwd->connectaddr,
  88. fwd->connectport);
  89. }
  90. }
  91. TRACE(("leave setup_localtcp"))
  92. }
  93. static int cli_localtcp(const char* listenaddr,
  94. unsigned int listenport,
  95. const char* remoteaddr,
  96. unsigned int remoteport) {
  97. struct TCPListener* tcpinfo = NULL;
  98. int ret;
  99. TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
  100. remoteport));
  101. tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
  102. tcpinfo->sendaddr = m_strdup(remoteaddr);
  103. tcpinfo->sendport = remoteport;
  104. if (listenaddr)
  105. {
  106. tcpinfo->listenaddr = m_strdup(listenaddr);
  107. }
  108. else
  109. {
  110. if (opts.listen_fwd_all) {
  111. tcpinfo->listenaddr = m_strdup("");
  112. } else {
  113. tcpinfo->listenaddr = m_strdup("localhost");
  114. }
  115. }
  116. tcpinfo->listenport = listenport;
  117. tcpinfo->chantype = &cli_chan_tcplocal;
  118. tcpinfo->tcp_type = direct;
  119. ret = listen_tcpfwd(tcpinfo, NULL);
  120. if (ret == DROPBEAR_FAILURE) {
  121. m_free(tcpinfo);
  122. }
  123. TRACE(("leave cli_localtcp: %d", ret))
  124. return ret;
  125. }
  126. #endif /* DROPBEAR_CLI_LOCALTCPFWD */
  127. #if DROPBEAR_CLI_REMOTETCPFWD
  128. static void send_msg_global_request_remotetcp(const char *addr, int port) {
  129. TRACE(("enter send_msg_global_request_remotetcp"))
  130. CHECKCLEARTOWRITE();
  131. buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
  132. buf_putstring(ses.writepayload, "tcpip-forward", 13);
  133. buf_putbyte(ses.writepayload, 1); /* want_reply */
  134. buf_putstring(ses.writepayload, addr, strlen(addr));
  135. buf_putint(ses.writepayload, port);
  136. encrypt_packet();
  137. TRACE(("leave send_msg_global_request_remotetcp"))
  138. }
  139. /* The only global success/failure messages are for remotetcp.
  140. * Since there isn't any identifier in these messages, we have to rely on them
  141. * being in the same order as we sent the requests. This is the ordering
  142. * of the cli_opts.remotefwds list.
  143. * If the requested remote port is 0 the listen port will be
  144. * dynamically allocated by the server and the port number will be returned
  145. * to client and the port number reported to the user. */
  146. void cli_recv_msg_request_success() {
  147. /* We just mark off that we have received the reply,
  148. * so that we can report failure for later ones. */
  149. m_list_elem * iter = NULL;
  150. for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
  151. struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
  152. if (!fwd->have_reply) {
  153. fwd->have_reply = 1;
  154. if (fwd->listenport == 0) {
  155. /* The server should let us know which port was allocated if we requested port 0 */
  156. int allocport = buf_getint(ses.payload);
  157. if (allocport > 0) {
  158. fwd->listenport = allocport;
  159. dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d",
  160. allocport, fwd->connectaddr, fwd->connectport);
  161. }
  162. }
  163. return;
  164. }
  165. }
  166. }
  167. void cli_recv_msg_request_failure() {
  168. m_list_elem *iter;
  169. for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
  170. struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
  171. if (!fwd->have_reply) {
  172. fwd->have_reply = 1;
  173. fwd_failed("Remote TCP forward request failed (port %d -> %s:%d)",
  174. fwd->listenport,
  175. fwd->connectaddr,
  176. fwd->connectport);
  177. return;
  178. }
  179. }
  180. }
  181. void setup_remotetcp() {
  182. m_list_elem *iter;
  183. TRACE(("enter setup_remotetcp"))
  184. for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
  185. struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
  186. if (!fwd->listenaddr)
  187. {
  188. /* we store the addresses so that we can compare them
  189. when the server sends them back */
  190. if (opts.listen_fwd_all) {
  191. fwd->listenaddr = m_strdup("");
  192. } else {
  193. fwd->listenaddr = m_strdup("localhost");
  194. }
  195. }
  196. send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
  197. }
  198. TRACE(("leave setup_remotetcp"))
  199. }
  200. static int newtcpforwarded(struct Channel * channel) {
  201. char *origaddr = NULL;
  202. unsigned int origport;
  203. m_list_elem * iter = NULL;
  204. struct TCPFwdEntry *fwd = NULL;
  205. char portstring[NI_MAXSERV];
  206. int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
  207. origaddr = buf_getstring(ses.payload, NULL);
  208. origport = buf_getint(ses.payload);
  209. /* Find which port corresponds. First try and match address as well as port,
  210. in case they want to forward different ports separately ... */
  211. for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
  212. fwd = (struct TCPFwdEntry*)iter->item;
  213. if (origport == fwd->listenport
  214. && strcmp(origaddr, fwd->listenaddr) == 0) {
  215. break;
  216. }
  217. }
  218. if (!iter)
  219. {
  220. /* ... otherwise try to generically match the only forwarded port
  221. without address (also handles ::1 vs 127.0.0.1 vs localhost case).
  222. rfc4254 is vague about the definition of "address that was connected" */
  223. for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
  224. fwd = (struct TCPFwdEntry*)iter->item;
  225. if (origport == fwd->listenport) {
  226. break;
  227. }
  228. }
  229. }
  230. if (iter == NULL || fwd == NULL) {
  231. /* We didn't request forwarding on that port */
  232. cleantext(origaddr);
  233. dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"",
  234. origaddr, origport);
  235. goto out;
  236. }
  237. snprintf(portstring, sizeof(portstring), "%u", fwd->connectport);
  238. channel->conn_pending = connect_remote(fwd->connectaddr, portstring, channel_connect_done,
  239. channel, NULL, NULL, DROPBEAR_PRIO_NORMAL);
  240. err = SSH_OPEN_IN_PROGRESS;
  241. out:
  242. m_free(origaddr);
  243. TRACE(("leave newtcpdirect: err %d", err))
  244. return err;
  245. }
  246. #endif /* DROPBEAR_CLI_REMOTETCPFWD */