Coverage Report

Created: 2024-09-08 06:28

/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
static int newstreamlocal(struct Channel * channel);
60
61
#if DROPBEAR_SVR_REMOTETCPFWD
62
static const struct ChanType svr_chan_tcpremote = {
63
  "forwarded-tcpip",
64
  NULL,
65
  NULL,
66
  NULL,
67
  NULL,
68
  NULL
69
};
70
71
/* At the moment this is completely used for tcp code (with the name reflecting
72
 * that). If new request types are added, this should be replaced with code
73
 * similar to the request-switching in chansession.c */
74
3.05k
void recv_msg_global_request_remotetcp() {
75
76
3.05k
  char* reqname = NULL;
77
3.05k
  unsigned int namelen;
78
3.05k
  unsigned int wantreply = 0;
79
3.05k
  int ret = DROPBEAR_FAILURE;
80
81
3.05k
  TRACE(("enter recv_msg_global_request_remotetcp"))
82
83
3.05k
  reqname = buf_getstring(ses.payload, &namelen);
84
3.05k
  wantreply = buf_getbool(ses.payload);
85
86
3.05k
  if (svr_opts.noremotetcp || !svr_pubkey_allows_tcpfwd()) {
87
0
    TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
88
0
    goto out;
89
0
  }
90
91
3.05k
  if (namelen > MAX_NAME_LEN) {
92
69
    TRACE(("name len is wrong: %d", namelen))
93
69
    goto out;
94
69
  }
95
96
2.98k
  if (strcmp("tcpip-forward", reqname) == 0) {
97
1.61k
    int allocated_listen_port = 0;
98
1.61k
    ret = svr_remotetcpreq(&allocated_listen_port);
99
    /* client expects-port-number-to-make-use-of-server-allocated-ports */
100
1.61k
    if (DROPBEAR_SUCCESS == ret) {
101
0
      CHECKCLEARTOWRITE();
102
0
      buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
103
0
      buf_putint(ses.writepayload, allocated_listen_port);
104
0
      encrypt_packet();
105
0
      wantreply = 0; /* avoid out: below sending another reply */
106
0
    }
107
1.61k
  } else if (strcmp("cancel-tcpip-forward", reqname) == 0) {
108
273
    ret = svr_cancelremotetcp();
109
1.09k
  } else {
110
1.09k
    TRACE(("reqname isn't tcpip-forward: '%s'", reqname))
111
1.09k
  }
112
113
3.04k
out:
114
3.04k
  if (wantreply) {
115
721
    if (ret == DROPBEAR_SUCCESS) {
116
0
      send_msg_request_success();
117
721
    } else {
118
721
      send_msg_request_failure();
119
721
    }
120
721
  }
121
122
3.04k
  m_free(reqname);
123
124
3.04k
  TRACE(("leave recv_msg_global_request"))
125
3.04k
}
126
127
0
static int matchtcp(const void* typedata1, const void* typedata2) {
128
129
0
  const struct TCPListener *info1 = (struct TCPListener*)typedata1;
130
0
  const struct TCPListener *info2 = (struct TCPListener*)typedata2;
131
132
0
  return (info1->listenport == info2->listenport)
133
0
      && (info1->chantype == info2->chantype)
134
0
      && (strcmp(info1->listenaddr, info2->listenaddr) == 0);
135
0
}
136
137
273
static int svr_cancelremotetcp() {
138
139
273
  int ret = DROPBEAR_FAILURE;
140
273
  char * bindaddr = NULL;
141
273
  unsigned int addrlen;
142
273
  unsigned int port;
143
273
  struct Listener * listener = NULL;
144
273
  struct TCPListener tcpinfo;
145
146
273
  TRACE(("enter cancelremotetcp"))
147
148
273
  bindaddr = buf_getstring(ses.payload, &addrlen);
149
273
  if (addrlen > MAX_HOST_LEN) {
150
70
    TRACE(("addr len too long: %d", addrlen))
151
70
    goto out;
152
70
  }
153
154
203
  port = buf_getint(ses.payload);
155
156
203
  tcpinfo.sendaddr = NULL;
157
203
  tcpinfo.sendport = 0;
158
203
  tcpinfo.listenaddr = bindaddr;
159
203
  tcpinfo.listenport = port;
160
203
  listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
161
203
  if (listener) {
162
0
    remove_listener( listener );
163
0
    ret = DROPBEAR_SUCCESS;
164
0
  }
165
166
271
out:
167
271
  m_free(bindaddr);
168
271
  TRACE(("leave cancelremotetcp"))
169
271
  return ret;
170
203
}
171
172
1.61k
static int svr_remotetcpreq(int *allocated_listen_port) {
173
174
1.61k
  int ret = DROPBEAR_FAILURE;
175
1.61k
  char * request_addr = NULL;
176
1.61k
  unsigned int addrlen;
177
1.61k
  struct TCPListener *tcpinfo = NULL;
178
1.61k
  unsigned int port;
179
1.61k
  struct Listener *listener = NULL;
180
181
1.61k
  TRACE(("enter remotetcpreq"))
182
183
1.61k
  request_addr = buf_getstring(ses.payload, &addrlen);
184
1.61k
  if (addrlen > MAX_HOST_LEN) {
185
70
    TRACE(("addr len too long: %d", addrlen))
186
70
    goto out;
187
70
  }
188
189
1.54k
  port = buf_getint(ses.payload);
190
191
1.54k
  if (port != 0) {
192
1.35k
    if (port < 1 || port > 65535) {
193
196
      TRACE(("invalid port: %d", port))
194
196
      goto out;
195
196
    }
196
197
1.15k
    if (!ses.allowprivport && port < IPPORT_RESERVED) {
198
191
      TRACE(("can't assign port < 1024 for non-root"))
199
191
      goto out;
200
191
    }
201
1.15k
  }
202
203
1.16k
  tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
204
1.16k
  tcpinfo->sendaddr = NULL;
205
1.16k
  tcpinfo->sendport = 0;
206
1.16k
  tcpinfo->listenport = port;
207
1.16k
  tcpinfo->chantype = &svr_chan_tcpremote;
208
1.16k
  tcpinfo->tcp_type = forwarded;
209
1.16k
  tcpinfo->interface = svr_opts.interface;
210
211
1.16k
  tcpinfo->request_listenaddr = request_addr;
212
1.16k
  if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) {
213
    /* NULL means "localhost only" */
214
1.15k
    tcpinfo->listenaddr = NULL;
215
1.15k
  }
216
4
  else
217
4
  {
218
4
    tcpinfo->listenaddr = m_strdup(request_addr);
219
4
  }
220
221
1.16k
  ret = listen_tcpfwd(tcpinfo, &listener);
222
1.16k
  if (DROPBEAR_SUCCESS == ret) {
223
0
    tcpinfo->listenport = get_sock_port(listener->socks[0]);
224
0
    *allocated_listen_port = tcpinfo->listenport;
225
0
  }
226
227
1.61k
out:
228
1.61k
  if (ret == DROPBEAR_FAILURE) {
229
    /* we only free it if a listener wasn't created, since the listener
230
     * has to remember it if it's to be cancelled */
231
1.61k
    m_free(request_addr);
232
1.61k
    m_free(tcpinfo);
233
1.61k
  }
234
235
1.61k
  TRACE(("leave remotetcpreq"))
236
237
1.61k
  return ret;
238
1.16k
}
239
240
#endif /* DROPBEAR_SVR_REMOTETCPFWD */
241
242
#if DROPBEAR_SVR_LOCALTCPFWD
243
244
const struct ChanType svr_chan_tcpdirect = {
245
  "direct-tcpip",
246
  newtcpdirect, /* init */
247
  NULL, /* checkclose */
248
  NULL, /* reqhandler */
249
  NULL, /* closehandler */
250
  NULL /* cleanup */
251
};
252
253
/* Called upon creating a new direct tcp channel (ie we connect out to an
254
 * address */
255
404
static int newtcpdirect(struct Channel * channel) {
256
257
404
  char* desthost = NULL;
258
404
  unsigned int destport;
259
404
  char* orighost = NULL;
260
404
  unsigned int origport;
261
404
  char portstring[NI_MAXSERV];
262
404
  unsigned int len;
263
404
  int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
264
265
404
  TRACE(("newtcpdirect channel %d", channel->index))
266
267
404
  if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
268
0
    TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
269
0
    goto out;
270
0
  }
271
272
404
  desthost = buf_getstring(ses.payload, &len);
273
404
  if (len > MAX_HOST_LEN) {
274
66
    TRACE(("leave newtcpdirect: desthost too long"))
275
66
    goto out;
276
66
  }
277
278
338
  destport = buf_getint(ses.payload);
279
  
280
338
  orighost = buf_getstring(ses.payload, &len);
281
338
  if (len > MAX_HOST_LEN) {
282
70
    TRACE(("leave newtcpdirect: orighost too long"))
283
70
    goto out;
284
70
  }
285
286
268
  origport = buf_getint(ses.payload);
287
288
  /* best be sure */
289
268
  if (origport > 65535 || destport > 65535) {
290
151
    TRACE(("leave newtcpdirect: port > 65535"))
291
151
    goto out;
292
151
  }
293
294
117
  if (!svr_pubkey_allows_local_tcpfwd(desthost, destport)) {
295
0
    TRACE(("leave newtcpdirect: local tcp forwarding not permitted to requested destination"));
296
0
    goto out;
297
0
  }
298
299
117
  snprintf(portstring, sizeof(portstring), "%u", destport);
300
117
  channel->conn_pending = connect_remote(desthost, portstring, channel_connect_done,
301
117
    channel, NULL, NULL, DROPBEAR_PRIO_NORMAL);
302
303
117
  err = SSH_OPEN_IN_PROGRESS;
304
305
389
out:
306
389
  m_free(desthost);
307
389
  m_free(orighost);
308
389
  TRACE(("leave newtcpdirect: err %d", err))
309
389
  return err;
310
117
}
311
312
#endif /* DROPBEAR_SVR_LOCALTCPFWD */
313
314
315
#if DROPBEAR_SVR_LOCALSTREAMFWD
316
317
const struct ChanType svr_chan_streamlocal = {
318
  "direct-streamlocal@openssh.com",
319
  newstreamlocal, /* init */
320
  NULL, /* checkclose */
321
  NULL, /* reqhandler */
322
  NULL, /* closehandler */
323
  NULL /* cleanup */
324
};
325
326
/* Called upon creating a new stream local channel (ie we connect out to an
327
 * address */
328
146
static int newstreamlocal(struct Channel * channel) {
329
330
  /*
331
  https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL#rev1.30
332
333
  byte    SSH_MSG_CHANNEL_OPEN
334
  string    "direct-streamlocal@openssh.com"
335
  uint32    sender channel
336
  uint32    initial window size
337
  uint32    maximum packet size
338
  string    socket path
339
  string    reserved
340
  uint32    reserved
341
  */
342
343
146
  char* destsocket = NULL;
344
146
  unsigned int len;
345
146
  int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
346
347
146
  TRACE(("streamlocal channel %d", channel->index))
348
349
146
  if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
350
0
    TRACE(("leave newstreamlocal: local unix forwarding disabled"))
351
0
    goto out;
352
0
  }
353
354
146
  destsocket = buf_getstring(ses.payload, &len);
355
146
  if (len > MAX_HOST_LEN) {
356
99
    TRACE(("leave streamlocal: destsocket too long"))
357
99
    goto out;
358
99
  }
359
360
47
  channel->conn_pending = connect_streamlocal(destsocket, channel_connect_done,
361
47
    channel, DROPBEAR_PRIO_NORMAL);
362
363
47
  err = SSH_OPEN_IN_PROGRESS;
364
365
145
out:
366
145
  m_free(destsocket);
367
145
  TRACE(("leave streamlocal: err %d", err))
368
145
  return err;
369
47
}
370
371
#endif /* DROPBEAR_SVR_LOCALSTREAMFWD */