svr-tcpfwd.c 7.2 KB

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