Coverage Report

Created: 2023-11-19 07:13

/src/dropbear/src/cli-tcpfwd.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Dropbear SSH
3
 * 
4
 * Copyright (c) 2002,2003 Matt Johnston
5
 * All rights reserved.
6
 * 
7
 * Permission is hereby granted, free of charge, to any person obtaining a copy
8
 * of this software and associated documentation files (the "Software"), to deal
9
 * in the Software without restriction, including without limitation the rights
10
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
 * copies of the Software, and to permit persons to whom the Software is
12
 * furnished to do so, subject to the following conditions:
13
 * 
14
 * The above copyright notice and this permission notice shall be included in
15
 * all copies or substantial portions of the Software.
16
 * 
17
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
 * SOFTWARE. */
24
25
#include "includes.h"
26
#include "dbutil.h"
27
#include "tcpfwd.h"
28
#include "channel.h"
29
#include "runopts.h"
30
#include "session.h"
31
#include "ssh.h"
32
#include "netio.h"
33
34
#if DROPBEAR_CLI_REMOTETCPFWD
35
static int newtcpforwarded(struct Channel * channel);
36
37
const struct ChanType cli_chan_tcpremote = {
38
  "forwarded-tcpip",
39
  newtcpforwarded,
40
  NULL,
41
  NULL,
42
  NULL,
43
  NULL
44
};
45
#endif
46
47
#if DROPBEAR_CLI_LOCALTCPFWD
48
static int cli_localtcp(const char* listenaddr, 
49
    unsigned int listenport, 
50
    const char* remoteaddr,
51
    unsigned int remoteport);
52
static const struct ChanType cli_chan_tcplocal = {
53
  "direct-tcpip",
54
  NULL,
55
  NULL,
56
  NULL,
57
  NULL,
58
  NULL
59
};
60
#endif
61
62
#if DROPBEAR_CLI_ANYTCPFWD
63
static void fwd_failed(const char* format, ...) ATTRIB_PRINTF(1,2);
64
static void fwd_failed(const char* format, ...)
65
0
{
66
0
  va_list param;
67
0
  va_start(param, format);
68
69
0
  if (cli_opts.exit_on_fwd_failure) {
70
0
    _dropbear_exit(EXIT_FAILURE, format, param);
71
0
  } else {
72
0
    _dropbear_log(LOG_WARNING, format, param);
73
0
  }
74
75
0
  va_end(param);
76
0
}
77
#endif
78
79
#if DROPBEAR_CLI_LOCALTCPFWD
80
0
void setup_localtcp() {
81
0
  m_list_elem *iter;
82
0
  int ret;
83
84
0
  TRACE(("enter setup_localtcp"))
85
86
0
  for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
87
0
    struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
88
0
    ret = cli_localtcp(
89
0
        fwd->listenaddr,
90
0
        fwd->listenport,
91
0
        fwd->connectaddr,
92
0
        fwd->connectport);
93
0
    if (ret == DROPBEAR_FAILURE) {
94
0
      fwd_failed("Failed local port forward %s:%d:%s:%d",
95
0
          fwd->listenaddr,
96
0
          fwd->listenport,
97
0
          fwd->connectaddr,
98
0
          fwd->connectport);
99
0
    }    
100
0
  }
101
0
  TRACE(("leave setup_localtcp"))
102
103
0
}
104
105
static int cli_localtcp(const char* listenaddr, 
106
    unsigned int listenport, 
107
    const char* remoteaddr,
108
0
    unsigned int remoteport) {
109
110
0
  struct TCPListener* tcpinfo = NULL;
111
0
  int ret;
112
113
0
  TRACE(("enter cli_localtcp: %d %s %d", listenport, remoteaddr,
114
0
        remoteport));
115
116
0
  tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
117
118
0
  tcpinfo->sendaddr = m_strdup(remoteaddr);
119
0
  tcpinfo->sendport = remoteport;
120
121
0
  if (listenaddr)
122
0
  {
123
0
    tcpinfo->listenaddr = m_strdup(listenaddr);
124
0
  }
125
0
  else
126
0
  {
127
0
    if (opts.listen_fwd_all) {
128
0
      tcpinfo->listenaddr = m_strdup("");
129
0
    } else {
130
0
      tcpinfo->listenaddr = m_strdup("localhost");
131
0
    }
132
0
  }
133
0
  tcpinfo->listenport = listenport;
134
135
0
  tcpinfo->chantype = &cli_chan_tcplocal;
136
0
  tcpinfo->tcp_type = direct;
137
138
0
  ret = listen_tcpfwd(tcpinfo, NULL);
139
140
0
  if (ret == DROPBEAR_FAILURE) {
141
0
    m_free(tcpinfo);
142
0
  }
143
0
  TRACE(("leave cli_localtcp: %d", ret))
144
0
  return ret;
145
0
}
146
#endif /* DROPBEAR_CLI_LOCALTCPFWD */
147
148
#if DROPBEAR_CLI_REMOTETCPFWD
149
0
static void send_msg_global_request_remotetcp(const char *addr, int port) {
150
151
0
  TRACE(("enter send_msg_global_request_remotetcp"))
152
153
0
  CHECKCLEARTOWRITE();
154
0
  buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
155
0
  buf_putstring(ses.writepayload, "tcpip-forward", 13);
156
0
  buf_putbyte(ses.writepayload, 1); /* want_reply */
157
0
  buf_putstring(ses.writepayload, addr, strlen(addr));
158
0
  buf_putint(ses.writepayload, port);
159
160
0
  encrypt_packet();
161
162
0
  TRACE(("leave send_msg_global_request_remotetcp"))
163
0
}
164
165
/* The only global success/failure messages are for remotetcp.
166
 * Since there isn't any identifier in these messages, we have to rely on them
167
 * being in the same order as we sent the requests. This is the ordering
168
 * of the cli_opts.remotefwds list.
169
 * If the requested remote port is 0 the listen port will be
170
 * dynamically allocated by the server and the port number will be returned
171
 * to client and the port number reported to the user. */
172
225
void cli_recv_msg_request_success() {
173
  /* We just mark off that we have received the reply,
174
   * so that we can report failure for later ones. */
175
225
  m_list_elem * iter = NULL;
176
225
  for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
177
0
    struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
178
0
    if (!fwd->have_reply) {
179
0
      fwd->have_reply = 1;
180
0
      if (fwd->listenport == 0) {
181
        /* The server should let us know which port was allocated if we requested port 0 */
182
0
        int allocport = buf_getint(ses.payload);
183
0
        if (allocport > 0) {
184
0
          fwd->listenport = allocport;
185
0
          dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d", 
186
0
              allocport, fwd->connectaddr, fwd->connectport);
187
0
        }
188
0
      }
189
0
      return;
190
0
    }
191
0
  }
192
225
}
193
194
197
void cli_recv_msg_request_failure() {
195
197
  m_list_elem *iter;
196
197
  for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
197
0
    struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
198
0
    if (!fwd->have_reply) {
199
0
      fwd->have_reply = 1;
200
0
      fwd_failed("Remote TCP forward request failed (port %d -> %s:%d)",
201
0
          fwd->listenport,
202
0
          fwd->connectaddr,
203
0
          fwd->connectport);
204
0
      return;
205
0
    }
206
0
  }
207
197
}
208
209
0
void setup_remotetcp() {
210
0
  m_list_elem *iter;
211
0
  TRACE(("enter setup_remotetcp"))
212
213
0
  for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
214
0
    struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
215
0
    if (!fwd->listenaddr)
216
0
    {
217
      /* we store the addresses so that we can compare them
218
         when the server sends them back */
219
0
      if (opts.listen_fwd_all) {
220
0
        fwd->listenaddr = m_strdup("");
221
0
      } else {
222
0
        fwd->listenaddr = m_strdup("localhost");
223
0
      }
224
0
    }
225
0
    send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
226
0
  }
227
228
0
  TRACE(("leave setup_remotetcp"))
229
0
}
230
231
197
static int newtcpforwarded(struct Channel * channel) {
232
233
197
  char *origaddr = NULL;
234
197
  unsigned int origport;
235
197
  m_list_elem * iter = NULL;
236
197
  struct TCPFwdEntry *fwd = NULL;
237
197
  char portstring[NI_MAXSERV];
238
197
  int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
239
240
197
  origaddr = buf_getstring(ses.payload, NULL);
241
197
  origport = buf_getint(ses.payload);
242
243
  /* Find which port corresponds. First try and match address as well as port,
244
  in case they want to forward different ports separately ... */
245
197
  for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
246
0
    fwd = (struct TCPFwdEntry*)iter->item;
247
0
    if (origport == fwd->listenport
248
0
        && strcmp(origaddr, fwd->listenaddr) == 0) {
249
0
      break;
250
0
    }
251
0
  }
252
253
197
  if (!iter)
254
195
  {
255
    /* ... otherwise try to generically match the only forwarded port 
256
    without address (also handles ::1 vs 127.0.0.1 vs localhost case).
257
    rfc4254 is vague about the definition of "address that was connected" */
258
195
    for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
259
0
      fwd = (struct TCPFwdEntry*)iter->item;
260
0
      if (origport == fwd->listenport) {
261
0
        break;
262
0
      }
263
0
    }
264
195
  }
265
266
267
197
  if (iter == NULL || fwd == NULL) {
268
    /* We didn't request forwarding on that port */
269
195
    cleantext(origaddr);
270
195
    dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", 
271
195
                origaddr, origport);
272
195
    goto out;
273
195
  }
274
275
2
  snprintf(portstring, sizeof(portstring), "%u", fwd->connectport);
276
2
  channel->conn_pending = connect_remote(fwd->connectaddr, portstring, channel_connect_done,
277
2
    channel, NULL, NULL, DROPBEAR_PRIO_NORMAL);
278
279
2
  err = SSH_OPEN_IN_PROGRESS;
280
281
195
out:
282
195
  m_free(origaddr);
283
195
  TRACE(("leave newtcpdirect: err %d", err))
284
195
  return err;
285
2
}
286
#endif /* DROPBEAR_CLI_REMOTETCPFWD */