diff options
author | Damien Miller <djm@mindrot.org> | 2017-07-21 14:38:16 +1000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2017-07-28 13:22:47 +1000 |
commit | c78e6eec78c88acf8d51db90ae05a3e39458603d (patch) | |
tree | 02c84858f139b6475a08182ae79d161bda5e0b93 /openbsd-compat/port-tun.c | |
parent | 2985d4062ebf4204bbd373456a810d558698f9f5 (diff) |
fix problems in tunnel forwarding portability code
This fixes a few problems in the tun forwarding code, mostly to do
with host/network byte order confusion.
Based on a report and patch by stepe AT centaurus.uberspace.de;
bz#2735; ok dtucker@
Diffstat (limited to 'openbsd-compat/port-tun.c')
-rw-r--r-- | openbsd-compat/port-tun.c | 83 |
1 files changed, 41 insertions, 42 deletions
diff --git a/openbsd-compat/port-tun.c b/openbsd-compat/port-tun.c index a444adf1d..a7a5d949a 100644 --- a/openbsd-compat/port-tun.c +++ b/openbsd-compat/port-tun.c | |||
@@ -199,49 +199,50 @@ sys_tun_open(int tun, int mode) | |||
199 | */ | 199 | */ |
200 | 200 | ||
201 | #if defined(SSH_TUN_FILTER) | 201 | #if defined(SSH_TUN_FILTER) |
202 | /* | ||
203 | * The tunnel forwarding protocol prepends the address family of forwarded | ||
204 | * IP packets using OpenBSD's numbers. | ||
205 | */ | ||
202 | #define OPENBSD_AF_INET 2 | 206 | #define OPENBSD_AF_INET 2 |
203 | #define OPENBSD_AF_INET6 24 | 207 | #define OPENBSD_AF_INET6 24 |
204 | 208 | ||
205 | int | 209 | int |
206 | sys_tun_infilter(struct Channel *c, char *buf, int len) | 210 | sys_tun_infilter(struct Channel *c, char *buf, int _len) |
207 | { | 211 | { |
212 | int r; | ||
213 | size_t len; | ||
214 | char *ptr = buf; | ||
208 | #if defined(SSH_TUN_PREPEND_AF) | 215 | #if defined(SSH_TUN_PREPEND_AF) |
209 | char rbuf[CHAN_RBUF]; | 216 | char rbuf[CHAN_RBUF]; |
210 | struct ip *iph; | 217 | struct ip iph; |
211 | #endif | 218 | #endif |
212 | u_int32_t *af; | 219 | #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF) |
213 | char *ptr = buf; | 220 | u_int32_t af; |
214 | int r; | ||
215 | |||
216 | #if defined(SSH_TUN_PREPEND_AF) | ||
217 | if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af))) | ||
218 | return (-1); | ||
219 | ptr = (char *)&rbuf[0]; | ||
220 | bcopy(buf, ptr + sizeof(u_int32_t), len); | ||
221 | len += sizeof(u_int32_t); | ||
222 | af = (u_int32_t *)ptr; | ||
223 | |||
224 | iph = (struct ip *)(ptr + sizeof(u_int32_t)); | ||
225 | switch (iph->ip_v) { | ||
226 | case 6: | ||
227 | *af = AF_INET6; | ||
228 | break; | ||
229 | case 4: | ||
230 | default: | ||
231 | *af = AF_INET; | ||
232 | break; | ||
233 | } | ||
234 | #endif | 221 | #endif |
235 | 222 | ||
236 | #if defined(SSH_TUN_COMPAT_AF) | 223 | /* XXX update channel input filter API to use unsigned length */ |
237 | if (len < (int)sizeof(u_int32_t)) | 224 | if (_len < 0) |
238 | return (-1); | 225 | return -1; |
226 | len = _len; | ||
239 | 227 | ||
240 | af = (u_int32_t *)ptr; | 228 | #if defined(SSH_TUN_PREPEND_AF) |
241 | if (*af == htonl(AF_INET6)) | 229 | if (len <= sizeof(iph) || len > sizeof(rbuf) - 4) |
242 | *af = htonl(OPENBSD_AF_INET6); | 230 | return -1; |
243 | else | 231 | /* Determine address family from packet IP header. */ |
244 | *af = htonl(OPENBSD_AF_INET); | 232 | memcpy(&iph, buf, sizeof(iph)); |
233 | af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET; | ||
234 | /* Prepend address family to packet using OpenBSD constants */ | ||
235 | memcpy(rbuf + 4, buf, len); | ||
236 | len += 4; | ||
237 | POKE_U32(rbuf, af); | ||
238 | ptr = rbuf; | ||
239 | #elif defined(SSH_TUN_COMPAT_AF) | ||
240 | /* Convert existing address family header to OpenBSD value */ | ||
241 | if (len <= 4) | ||
242 | return -1; | ||
243 | af = PEEK_U32(buf); | ||
244 | /* Put it back */ | ||
245 | POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET); | ||
245 | #endif | 246 | #endif |
246 | 247 | ||
247 | if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0) | 248 | if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0) |
@@ -253,7 +254,7 @@ u_char * | |||
253 | sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen) | 254 | sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen) |
254 | { | 255 | { |
255 | u_char *buf; | 256 | u_char *buf; |
256 | u_int32_t *af; | 257 | u_int32_t af; |
257 | int r; | 258 | int r; |
258 | size_t xxx_dlen; | 259 | size_t xxx_dlen; |
259 | 260 | ||
@@ -262,21 +263,19 @@ sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen) | |||
262 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); | 263 | fatal("%s: buffer error: %s", __func__, ssh_err(r)); |
263 | if (dlen != NULL) | 264 | if (dlen != NULL) |
264 | *dlen = xxx_dlen; | 265 | *dlen = xxx_dlen; |
265 | if (*dlen < sizeof(*af)) | 266 | if (*dlen < sizeof(af)) |
266 | return (NULL); | 267 | return (NULL); |
267 | buf = *data; | 268 | buf = *data; |
268 | 269 | ||
269 | #if defined(SSH_TUN_PREPEND_AF) | 270 | #if defined(SSH_TUN_PREPEND_AF) |
270 | *dlen -= sizeof(u_int32_t); | 271 | /* skip address family */ |
271 | buf = *data + sizeof(u_int32_t); | 272 | *dlen -= sizeof(af); |
273 | buf = *data + sizeof(af); | ||
272 | #elif defined(SSH_TUN_COMPAT_AF) | 274 | #elif defined(SSH_TUN_COMPAT_AF) |
273 | af = ntohl(*(u_int32_t *)buf); | 275 | /* translate address family */ |
274 | if (*af == OPENBSD_AF_INET6) | 276 | af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET; |
275 | *af = htonl(AF_INET6); | 277 | POKE_U32(buf, af); |
276 | else | ||
277 | *af = htonl(AF_INET); | ||
278 | #endif | 278 | #endif |
279 | |||
280 | return (buf); | 279 | return (buf); |
281 | } | 280 | } |
282 | #endif /* SSH_TUN_FILTER */ | 281 | #endif /* SSH_TUN_FILTER */ |