/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 | | |