svr-tcpfwd.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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 "ssh.h"
  27. #include "tcpfwd.h"
  28. #include "dbutil.h"
  29. #include "session.h"
  30. #include "buffer.h"
  31. #include "packet.h"
  32. #include "listener.h"
  33. #include "runopts.h"
  34. #include "auth.h"
  35. #include "netio.h"
  36. #if !DROPBEAR_SVR_REMOTETCPFWD
  37. /* This is better than SSH_MSG_UNIMPLEMENTED */
  38. void recv_msg_global_request_remotetcp() {
  39. unsigned int wantreply = 0;
  40. TRACE(("recv_msg_global_request_remotetcp: remote tcp forwarding not compiled in"))
  41. buf_eatstring(ses.payload);
  42. wantreply = buf_getbool(ses.payload);
  43. if (wantreply) {
  44. send_msg_request_failure();
  45. }
  46. }
  47. /* */
  48. #endif /* !DROPBEAR_SVR_REMOTETCPFWD */
  49. static int svr_cancelremotetcp(void);
  50. static int svr_remotetcpreq(int *allocated_listen_port);
  51. static int newtcpdirect(struct Channel * channel);
  52. #if DROPBEAR_SVR_REMOTETCPFWD
  53. static const struct ChanType svr_chan_tcpremote = {
  54. "forwarded-tcpip",
  55. NULL,
  56. NULL,
  57. NULL,
  58. NULL,
  59. NULL
  60. };
  61. /* At the moment this is completely used for tcp code (with the name reflecting
  62. * that). If new request types are added, this should be replaced with code
  63. * similar to the request-switching in chansession.c */
  64. void recv_msg_global_request_remotetcp() {
  65. char* reqname = NULL;
  66. unsigned int namelen;
  67. unsigned int wantreply = 0;
  68. int ret = DROPBEAR_FAILURE;
  69. TRACE(("enter recv_msg_global_request_remotetcp"))
  70. if (svr_opts.noremotetcp || !svr_pubkey_allows_tcpfwd()) {
  71. TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
  72. goto out;
  73. }
  74. reqname = buf_getstring(ses.payload, &namelen);
  75. wantreply = buf_getbool(ses.payload);
  76. if (namelen > MAX_NAME_LEN) {
  77. TRACE(("name len is wrong: %d", namelen))
  78. goto out;
  79. }
  80. if (strcmp("tcpip-forward", reqname) == 0) {
  81. int allocated_listen_port = 0;
  82. ret = svr_remotetcpreq(&allocated_listen_port);
  83. /* client expects-port-number-to-make-use-of-server-allocated-ports */
  84. if (DROPBEAR_SUCCESS == ret) {
  85. CHECKCLEARTOWRITE();
  86. buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
  87. buf_putint(ses.writepayload, allocated_listen_port);
  88. encrypt_packet();
  89. wantreply = 0; /* avoid out: below sending another reply */
  90. }
  91. } else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
  92. ret = svr_cancelremotetcp();
  93. } else {
  94. TRACE(("reqname isn't tcpip-forward: '%s'", reqname))
  95. }
  96. out:
  97. if (wantreply) {
  98. if (ret == DROPBEAR_SUCCESS) {
  99. send_msg_request_success();
  100. } else {
  101. send_msg_request_failure();
  102. }
  103. }
  104. m_free(reqname);
  105. TRACE(("leave recv_msg_global_request"))
  106. }
  107. static int matchtcp(const void* typedata1, const void* typedata2) {
  108. const struct TCPListener *info1 = (struct TCPListener*)typedata1;
  109. const struct TCPListener *info2 = (struct TCPListener*)typedata2;
  110. return (info1->listenport == info2->listenport)
  111. && (info1->chantype == info2->chantype)
  112. && (strcmp(info1->listenaddr, info2->listenaddr) == 0);
  113. }
  114. static int svr_cancelremotetcp() {
  115. int ret = DROPBEAR_FAILURE;
  116. char * bindaddr = NULL;
  117. unsigned int addrlen;
  118. unsigned int port;
  119. struct Listener * listener = NULL;
  120. struct TCPListener tcpinfo;
  121. TRACE(("enter cancelremotetcp"))
  122. bindaddr = buf_getstring(ses.payload, &addrlen);
  123. if (addrlen > MAX_HOST_LEN) {
  124. TRACE(("addr len too long: %d", addrlen))
  125. goto out;
  126. }
  127. port = buf_getint(ses.payload);
  128. tcpinfo.sendaddr = NULL;
  129. tcpinfo.sendport = 0;
  130. tcpinfo.listenaddr = bindaddr;
  131. tcpinfo.listenport = port;
  132. listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
  133. if (listener) {
  134. remove_listener( listener );
  135. ret = DROPBEAR_SUCCESS;
  136. }
  137. out:
  138. m_free(bindaddr);
  139. TRACE(("leave cancelremotetcp"))
  140. return ret;
  141. }
  142. static int svr_remotetcpreq(int *allocated_listen_port) {
  143. int ret = DROPBEAR_FAILURE;
  144. char * request_addr = NULL;
  145. unsigned int addrlen;
  146. struct TCPListener *tcpinfo = NULL;
  147. unsigned int port;
  148. struct Listener *listener = NULL;
  149. TRACE(("enter remotetcpreq"))
  150. request_addr = buf_getstring(ses.payload, &addrlen);
  151. if (addrlen > MAX_HOST_LEN) {
  152. TRACE(("addr len too long: %d", addrlen))
  153. goto out;
  154. }
  155. port = buf_getint(ses.payload);
  156. if (port != 0) {
  157. if (port < 1 || port > 65535) {
  158. TRACE(("invalid port: %d", port))
  159. goto out;
  160. }
  161. if (!ses.allowprivport && port < IPPORT_RESERVED) {
  162. TRACE(("can't assign port < 1024 for non-root"))
  163. goto out;
  164. }
  165. }
  166. tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
  167. tcpinfo->sendaddr = NULL;
  168. tcpinfo->sendport = 0;
  169. tcpinfo->listenport = port;
  170. tcpinfo->chantype = &svr_chan_tcpremote;
  171. tcpinfo->tcp_type = forwarded;
  172. tcpinfo->request_listenaddr = request_addr;
  173. if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) {
  174. /* NULL means "localhost only" */
  175. tcpinfo->listenaddr = NULL;
  176. }
  177. else
  178. {
  179. tcpinfo->listenaddr = m_strdup(request_addr);
  180. }
  181. ret = listen_tcpfwd(tcpinfo, &listener);
  182. if (DROPBEAR_SUCCESS == ret) {
  183. tcpinfo->listenport = get_sock_port(listener->socks[0]);
  184. *allocated_listen_port = tcpinfo->listenport;
  185. }
  186. out:
  187. if (ret == DROPBEAR_FAILURE) {
  188. /* we only free it if a listener wasn't created, since the listener
  189. * has to remember it if it's to be cancelled */
  190. m_free(request_addr);
  191. m_free(tcpinfo);
  192. }
  193. TRACE(("leave remotetcpreq"))
  194. return ret;
  195. }
  196. #endif /* DROPBEAR_SVR_REMOTETCPFWD */
  197. #if DROPBEAR_SVR_LOCALTCPFWD
  198. const struct ChanType svr_chan_tcpdirect = {
  199. "direct-tcpip",
  200. newtcpdirect, /* init */
  201. NULL, /* checkclose */
  202. NULL, /* reqhandler */
  203. NULL, /* closehandler */
  204. NULL /* cleanup */
  205. };
  206. /* Called upon creating a new direct tcp channel (ie we connect out to an
  207. * address */
  208. static int newtcpdirect(struct Channel * channel) {
  209. char* desthost = NULL;
  210. unsigned int destport;
  211. char* orighost = NULL;
  212. unsigned int origport;
  213. char portstring[NI_MAXSERV];
  214. unsigned int len;
  215. int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
  216. TRACE(("newtcpdirect channel %d", channel->index))
  217. if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
  218. TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
  219. goto out;
  220. }
  221. desthost = buf_getstring(ses.payload, &len);
  222. if (len > MAX_HOST_LEN) {
  223. TRACE(("leave newtcpdirect: desthost too long"))
  224. goto out;
  225. }
  226. destport = buf_getint(ses.payload);
  227. orighost = buf_getstring(ses.payload, &len);
  228. if (len > MAX_HOST_LEN) {
  229. TRACE(("leave newtcpdirect: orighost too long"))
  230. goto out;
  231. }
  232. origport = buf_getint(ses.payload);
  233. /* best be sure */
  234. if (origport > 65535 || destport > 65535) {
  235. TRACE(("leave newtcpdirect: port > 65535"))
  236. goto out;
  237. }
  238. snprintf(portstring, sizeof(portstring), "%u", destport);
  239. channel->conn_pending = connect_remote(desthost, portstring, channel_connect_done,
  240. channel, NULL, NULL, DROPBEAR_PRIO_NORMAL);
  241. err = SSH_OPEN_IN_PROGRESS;
  242. out:
  243. m_free(desthost);
  244. m_free(orighost);
  245. TRACE(("leave newtcpdirect: err %d", err))
  246. return err;
  247. }
  248. #endif /* DROPBEAR_SVR_LOCALTCPFWD */