Coverage Report

Created: 2026-07-15 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dropbear/src/svr-tcpfwd.c
Line
Count
Source
1
#include "includes.h"
2
#include "ssh.h"
3
#include "forward.h"
4
#include "dbutil.h"
5
#include "session.h"
6
#include "buffer.h"
7
#include "packet.h"
8
#include "listener.h"
9
#include "runopts.h"
10
#include "auth.h"
11
#include "netio.h"
12
13
#if DROPBEAR_SVR_REMOTETCPFWD
14
15
static const struct ChanType svr_chan_tcpremote = {
16
    "forwarded-tcpip",
17
    NULL,
18
    NULL,
19
    NULL,
20
    NULL,
21
    NULL
22
};
23
24
0
static int matchtcp(const void* typedata1, const void* typedata2) {
25
26
0
    const struct FwdListener *info1 = (struct FwdListener*)typedata1;
27
0
    const struct FwdListener *info2 = (struct FwdListener*)typedata2;
28
29
0
    return (info1->listenport == info2->listenport)
30
0
            && (strcmp(info1->request_listenaddr, info2->request_listenaddr) == 0);
31
0
}
32
33
0
int svr_cancelremotetcp(void) {
34
35
0
    int ret = DROPBEAR_FAILURE;
36
0
    char * request_addr = NULL;
37
0
    unsigned int addrlen;
38
0
    unsigned int port;
39
0
    struct Listener * listener = NULL;
40
0
    struct FwdListener tcpinfo;
41
42
0
    TRACE(("enter cancelremotetcp"))
43
44
0
    request_addr = buf_getstring(ses.payload, &addrlen);
45
0
    if (addrlen > MAX_HOST_LEN) {
46
0
        TRACE(("addr len too long: %d", addrlen))
47
0
        goto out;
48
0
    }
49
50
0
    port = buf_getint(ses.payload);
51
52
0
    memset(&tcpinfo, 0x0, sizeof(tcpinfo));
53
0
    tcpinfo.request_listenaddr = request_addr;
54
0
    tcpinfo.listenport = port;
55
0
    listener = get_listener(LISTENER_TYPE_TCPFORWARDED, &tcpinfo, matchtcp);
56
0
    if (listener) {
57
0
        remove_listener(listener);
58
0
        ret = DROPBEAR_SUCCESS;
59
0
    }
60
61
0
out:
62
0
    m_free(request_addr);
63
0
    TRACE(("leave cancelremotetcp"))
64
0
    return ret;
65
0
}
66
67
/* Will send its own reply if needed. */
68
0
int svr_remotetcpreq(int wantreply) {
69
70
0
    int ret = DROPBEAR_FAILURE;
71
0
    char * request_addr = NULL;
72
0
    unsigned int addrlen;
73
0
    struct FwdListener *tcpinfo = NULL;
74
0
    unsigned int port;
75
0
    struct Listener *listener = NULL;
76
77
0
    TRACE(("enter remotetcpreq"))
78
79
0
    request_addr = buf_getstring(ses.payload, &addrlen);
80
0
    if (addrlen > MAX_HOST_LEN) {
81
0
        TRACE(("addr len too long: %d", addrlen))
82
0
        goto out;
83
0
    }
84
85
0
    port = buf_getint(ses.payload);
86
87
0
    if (port != 0) {
88
0
        if (port < 1 || port > 65535) {
89
0
            TRACE(("invalid port: %d", port))
90
0
            goto out;
91
0
        }
92
93
0
        if (!ses.allowprivport && port < IPPORT_RESERVED) {
94
0
            TRACE(("can't assign port < 1024 for non-root"))
95
0
            goto out;
96
0
        }
97
0
    }
98
99
0
    if (!svr_pubkey_allows_remote_tcpfwd(request_addr, port)) {
100
0
        TRACE(("remote tcp forwarding listen address not permitted"));
101
0
        goto out;
102
0
    }
103
104
0
    tcpinfo = (struct FwdListener*)m_malloc(sizeof(struct FwdListener));
105
0
    tcpinfo->sendaddr = NULL;
106
0
    tcpinfo->sendport = 0;
107
0
    tcpinfo->listenport = port;
108
0
    tcpinfo->chantype = &svr_chan_tcpremote;
109
0
    tcpinfo->fwd_type = forwarded;
110
0
    tcpinfo->interface = svr_opts.interface;
111
112
0
    tcpinfo->request_listenaddr = request_addr;
113
0
    if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) {
114
        /* NULL means "localhost only" */
115
0
        tcpinfo->listenaddr = NULL;
116
0
    }
117
0
    else
118
0
    {
119
0
        tcpinfo->listenaddr = m_strdup(request_addr);
120
0
    }
121
122
0
    ret = listen_tcpfwd(tcpinfo, &listener);
123
0
    if (DROPBEAR_SUCCESS == ret) {
124
0
        tcpinfo->listenport = get_sock_port(listener->socks[0]);
125
0
    }
126
127
0
out:
128
    /* Send a reply if needed */
129
0
    if (wantreply) {
130
0
        CHECKCLEARTOWRITE();
131
0
        if (ret == DROPBEAR_SUCCESS) {
132
0
            buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_SUCCESS);
133
0
            if (port == 0) {
134
                /* port is only included if allocated */
135
0
                buf_putint(ses.writepayload, tcpinfo->listenport);
136
0
            }
137
0
        } else {
138
0
            buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
139
0
        }
140
0
        encrypt_packet();
141
0
    }
142
143
0
    if (ret == DROPBEAR_FAILURE) {
144
        /* we only free it if a listener wasn't created, since the listener
145
         * has to remember it if it's to be cancelled */
146
0
        m_free(request_addr);
147
0
        m_free(tcpinfo);
148
0
    }
149
150
0
    TRACE(("leave remotetcpreq"))
151
152
0
    return ret;
153
0
}
154
155
156
#endif /* DROPBEAR_SVR_REMOTETCPFWD */
157
158
159
#if DROPBEAR_SVR_LOCALTCPFWD
160
161
/* Called upon creating a new direct tcp channel (ie we connect out to an
162
 * address */
163
0
static int newtcpdirect(struct Channel * channel) {
164
165
0
    char* desthost = NULL;
166
0
    unsigned int destport;
167
0
    char* orighost = NULL;
168
0
    unsigned int origport;
169
0
    char portstring[NI_MAXSERV];
170
0
    unsigned int len;
171
0
    int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
172
173
0
    TRACE(("newtcpdirect channel %d", channel->index))
174
175
0
    if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
176
0
        TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
177
0
        goto out;
178
0
    }
179
180
0
    desthost = buf_getstring(ses.payload, &len);
181
0
    if (len > MAX_HOST_LEN) {
182
0
        TRACE(("leave newtcpdirect: desthost too long"))
183
0
        goto out;
184
0
    }
185
186
0
    destport = buf_getint(ses.payload);
187
    
188
0
    orighost = buf_getstring(ses.payload, &len);
189
0
    if (len > MAX_HOST_LEN) {
190
0
        TRACE(("leave newtcpdirect: orighost too long"))
191
0
        goto out;
192
0
    }
193
194
0
    origport = buf_getint(ses.payload);
195
196
    /* best be sure */
197
0
    if (origport > 65535 || destport > 65535) {
198
0
        TRACE(("leave newtcpdirect: port > 65535"))
199
0
        goto out;
200
0
    }
201
202
0
    if (!svr_pubkey_allows_local_tcpfwd(desthost, destport)) {
203
0
        TRACE(("leave newtcpdirect: local tcp forwarding not permitted to requested destination"));
204
0
        goto out;
205
0
    }
206
207
0
    snprintf(portstring, sizeof(portstring), "%u", destport);
208
0
    channel->conn_pending = connect_remote(desthost, portstring, channel_connect_done,
209
0
        channel, NULL, NULL, DROPBEAR_PRIO_NORMAL);
210
211
0
    err = SSH_OPEN_IN_PROGRESS;
212
213
0
out:
214
0
    m_free(desthost);
215
    m_free(orighost);
216
0
    TRACE(("leave newtcpdirect: err %d", err))
217
0
    return err;
218
0
}
219
220
const struct ChanType svr_chan_tcpdirect = {
221
    "direct-tcpip",
222
    newtcpdirect, /* init */
223
    NULL, /* checkclose */
224
    NULL, /* reqhandler */
225
    NULL, /* closehandler */
226
    NULL /* cleanup */
227
};
228
229
#endif /* DROPBEAR_SVR_LOCALTCPFWD */
230