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 26 #include "includes.h" 27 #include "ssh.h" 28 #include "tcpfwd.h" 29 #include "dbutil.h" 30 #include "session.h" 31 #include "buffer.h" 32 #include "packet.h" 33 #include "listener.h" 34 #include "runopts.h" 35 36 #ifdef ENABLE_SVR_REMOTETCPFWD 37 38 static void send_msg_request_success(); 39 static void send_msg_request_failure(); 40 static int svr_cancelremotetcp(); 41 static int svr_remotetcpreq(); 42 static int newtcpdirect(struct Channel * channel); 43 44 45 const struct ChanType svr_chan_tcpdirect = { 46 1, /* sepfds */ 47 "direct-tcpip", 48 newtcpdirect, /* init */ 49 NULL, /* checkclose */ 50 NULL, /* reqhandler */ 51 NULL /* closehandler */ 52 }; 53 54 static const struct ChanType svr_chan_tcpremote = { 55 1, /* sepfds */ 56 "forwarded-tcpip", 57 NULL, 58 NULL, 59 NULL, 60 NULL 61 }; 62 63 /* At the moment this is completely used for tcp code (with the name reflecting 64 * that). If new request types are added, this should be replaced with code 65 * similar to the request-switching in chansession.c */ 66 void recv_msg_global_request_remotetcp() { 67 68 unsigned char* reqname = NULL; 69 unsigned int namelen; 70 unsigned int wantreply = 0; 71 int ret = DROPBEAR_FAILURE; 72 73 TRACE(("enter recv_msg_global_request_remotetcp")) 74 75 if (svr_opts.noremotetcp) { 76 TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled")) 77 goto out; 78 } 79 80 reqname = buf_getstring(ses.payload, &namelen); 81 wantreply = buf_getbool(ses.payload); 82 83 if (namelen > MAX_NAME_LEN) { 84 TRACE(("name len is wrong: %d", namelen)) 85 goto out; 86 } 87 88 if (strcmp("tcpip-forward", reqname) == 0) { 89 ret = svr_remotetcpreq(); 90 } else if (strcmp("cancel-tcpip-forward", reqname) == 0) { 91 ret = svr_cancelremotetcp(); 92 } else { 93 TRACE(("reqname isn't tcpip-forward: '%s'", reqname)) 94 } 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 105 m_free(reqname); 106 107 TRACE(("leave recv_msg_global_request")) 108 } 109 110 111 static void send_msg_request_success() { 112 113 CHECKCLEARTOWRITE(); 114 buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS); 115 encrypt_packet(); 116 117 } 118 119 static void send_msg_request_failure() { 120 121 CHECKCLEARTOWRITE(); 122 buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); 123 encrypt_packet(); 124 125 } 126 127 static int matchtcp(void* typedata1, void* typedata2) { 128 129 const struct TCPListener *info1 = (struct TCPListener*)typedata1; 130 const struct TCPListener *info2 = (struct TCPListener*)typedata2; 131 132 return (info1->listenport == info2->listenport) 133 && (info1->chantype == info2->chantype) 134 && (strcmp(info1->listenaddr, info2->listenaddr) == 0); 135 } 136 137 static int svr_cancelremotetcp() { 138 139 int ret = DROPBEAR_FAILURE; 140 unsigned char * bindaddr = NULL; 141 unsigned int addrlen; 142 unsigned int port; 143 struct Listener * listener = NULL; 144 struct TCPListener tcpinfo; 145 146 TRACE(("enter cancelremotetcp")) 147 148 bindaddr = buf_getstring(ses.payload, &addrlen); 149 if (addrlen > MAX_IP_LEN) { 150 TRACE(("addr len too long: %d", addrlen)) 151 goto out; 152 } 153 154 port = buf_getint(ses.payload); 155 156 tcpinfo.sendaddr = NULL; 157 tcpinfo.sendport = 0; 158 tcpinfo.listenaddr = bindaddr; 159 tcpinfo.listenport = port; 160 listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp); 161 if (listener) { 162 remove_listener( listener ); 163 ret = DROPBEAR_SUCCESS; 164 } 165 166 out: 167 m_free(bindaddr); 168 TRACE(("leave cancelremotetcp")) 169 return ret; 170 } 171 172 static int svr_remotetcpreq() { 173 174 int ret = DROPBEAR_FAILURE; 175 unsigned char * bindaddr = NULL; 176 unsigned int addrlen; 177 struct TCPListener *tcpinfo = NULL; 178 unsigned int port; 179 180 TRACE(("enter remotetcpreq")) 181 182 bindaddr = buf_getstring(ses.payload, &addrlen); 183 if (addrlen > MAX_IP_LEN) { 184 TRACE(("addr len too long: %d", addrlen)) 185 goto out; 186 } 187 188 port = buf_getint(ses.payload); 189 190 if (port == 0) { 191 dropbear_log(LOG_INFO, "Server chosen tcpfwd ports are unsupported"); 192 goto out; 193 } 194 195 if (port < 1 || port > 65535) { 196 TRACE(("invalid port: %d", port)) 197 goto out; 198 } 199 200 if (!ses.allowprivport && port < IPPORT_RESERVED) { 201 TRACE(("can't assign port < 1024 for non-root")) 202 goto out; 203 } 204 205 tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener)); 206 tcpinfo->sendaddr = NULL; 207 tcpinfo->sendport = 0; 208 tcpinfo->listenaddr = bindaddr; 209 tcpinfo->listenport = port; 210 tcpinfo->chantype = &svr_chan_tcpremote; 211 tcpinfo->tcp_type = forwarded; 212 213 ret = listen_tcpfwd(tcpinfo); 214 215 out: 216 if (ret == DROPBEAR_FAILURE) { 217 /* we only free it if a listener wasn't created, since the listener 218 * has to remember it if it's to be cancelled */ 219 m_free(bindaddr); 220 m_free(tcpinfo); 221 } 222 TRACE(("leave remotetcpreq")) 223 return ret; 224 } 225 226 /* Called upon creating a new direct tcp channel (ie we connect out to an 227 * address */ 228 static int newtcpdirect(struct Channel * channel) { 229 230 unsigned char* desthost = NULL; 231 unsigned int destport; 232 unsigned char* orighost = NULL; 233 unsigned int origport; 234 char portstring[NI_MAXSERV]; 235 int sock; 236 int len; 237 int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED; 238 239 if (svr_opts.nolocaltcp) { 240 TRACE(("leave newtcpdirect: local tcp forwarding disabled")) 241 goto out; 242 } 243 244 desthost = buf_getstring(ses.payload, &len); 245 if (len > MAX_HOST_LEN) { 246 TRACE(("leave newtcpdirect: desthost too long")) 247 goto out; 248 } 249 250 destport = buf_getint(ses.payload); 251 252 orighost = buf_getstring(ses.payload, &len); 253 if (len > MAX_HOST_LEN) { 254 TRACE(("leave newtcpdirect: orighost too long")) 255 goto out; 256 } 257 258 origport = buf_getint(ses.payload); 259 260 /* best be sure */ 261 if (origport > 65535 || destport > 65535) { 262 TRACE(("leave newtcpdirect: port > 65535")) 263 goto out; 264 } 265 266 snprintf(portstring, sizeof(portstring), "%d", destport); 267 sock = connect_remote(desthost, portstring, 1, NULL); 268 if (sock < 0) { 269 err = SSH_OPEN_CONNECT_FAILED; 270 TRACE(("leave newtcpdirect: sock failed")) 271 goto out; 272 } 273 274 ses.maxfd = MAX(ses.maxfd, sock); 275 276 /* We don't set readfd, that will get set after the connection's 277 * progress succeeds */ 278 channel->writefd = sock; 279 channel->initconn = 1; 280 281 err = SSH_OPEN_IN_PROGRESS; 282 283 out: 284 m_free(desthost); 285 m_free(orighost); 286 TRACE(("leave newtcpdirect: err %d", err)) 287 return err; 288 } 289 290 #endif 291