Coverage Report

Created: 2023-09-25 06:08

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