summaryrefslogtreecommitdiffstats
path: root/openbsd-compat
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2017-07-21 14:38:16 +1000
committerDamien Miller <djm@mindrot.org>2017-07-28 13:22:47 +1000
commitc78e6eec78c88acf8d51db90ae05a3e39458603d (patch)
tree02c84858f139b6475a08182ae79d161bda5e0b93 /openbsd-compat
parent2985d4062ebf4204bbd373456a810d558698f9f5 (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')
-rw-r--r--openbsd-compat/port-tun.c83
1 files changed, 41 insertions, 42 deletions
diff --git a/openbsd-compat/port-tun.c b/openbsd-compat/port-tun.c
index a444adf1..a7a5d949 100644
--- a/openbsd-compat/port-tun.c
+++ b/openbsd-compat/port-tun.c
@@ -199,49 +199,50 @@ sys_tun_open(int tun, int mode)
*/
#if defined(SSH_TUN_FILTER)
+/*
+ * The tunnel forwarding protocol prepends the address family of forwarded
+ * IP packets using OpenBSD's numbers.
+ */
#define OPENBSD_AF_INET 2
#define OPENBSD_AF_INET6 24
int
-sys_tun_infilter(struct Channel *c, char *buf, int len)
+sys_tun_infilter(struct Channel *c, char *buf, int _len)
{
+ int r;
+ size_t len;
+ char *ptr = buf;
#if defined(SSH_TUN_PREPEND_AF)
char rbuf[CHAN_RBUF];
- struct ip *iph;
+ struct ip iph;
#endif
- u_int32_t *af;
- char *ptr = buf;
- int r;
-
-#if defined(SSH_TUN_PREPEND_AF)
- if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af)))
- return (-1);
- ptr = (char *)&rbuf[0];
- bcopy(buf, ptr + sizeof(u_int32_t), len);
- len += sizeof(u_int32_t);
- af = (u_int32_t *)ptr;
-
- iph = (struct ip *)(ptr + sizeof(u_int32_t));
- switch (iph->ip_v) {
- case 6:
- *af = AF_INET6;
- break;
- case 4:
- default:
- *af = AF_INET;
- break;
- }
+#if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF)
+ u_int32_t af;
#endif
-#if defined(SSH_TUN_COMPAT_AF)
- if (len < (int)sizeof(u_int32_t))
- return (-1);
+ /* XXX update channel input filter API to use unsigned length */
+ if (_len < 0)
+ return -1;
+ len = _len;
- af = (u_int32_t *)ptr;
- if (*af == htonl(AF_INET6))
- *af = htonl(OPENBSD_AF_INET6);
- else
- *af = htonl(OPENBSD_AF_INET);
+#if defined(SSH_TUN_PREPEND_AF)
+ if (len <= sizeof(iph) || len > sizeof(rbuf) - 4)
+ return -1;
+ /* Determine address family from packet IP header. */
+ memcpy(&iph, buf, sizeof(iph));
+ af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET;
+ /* Prepend address family to packet using OpenBSD constants */
+ memcpy(rbuf + 4, buf, len);
+ len += 4;
+ POKE_U32(rbuf, af);
+ ptr = rbuf;
+#elif defined(SSH_TUN_COMPAT_AF)
+ /* Convert existing address family header to OpenBSD value */
+ if (len <= 4)
+ return -1;
+ af = PEEK_U32(buf);
+ /* Put it back */
+ POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET);
#endif
if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0)
@@ -253,7 +254,7 @@ u_char *
sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen)
{
u_char *buf;
- u_int32_t *af;
+ u_int32_t af;
int r;
size_t xxx_dlen;
@@ -262,21 +263,19 @@ sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen)
fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (dlen != NULL)
*dlen = xxx_dlen;
- if (*dlen < sizeof(*af))
+ if (*dlen < sizeof(af))
return (NULL);
buf = *data;
#if defined(SSH_TUN_PREPEND_AF)
- *dlen -= sizeof(u_int32_t);
- buf = *data + sizeof(u_int32_t);
+ /* skip address family */
+ *dlen -= sizeof(af);
+ buf = *data + sizeof(af);
#elif defined(SSH_TUN_COMPAT_AF)
- af = ntohl(*(u_int32_t *)buf);
- if (*af == OPENBSD_AF_INET6)
- *af = htonl(AF_INET6);
- else
- *af = htonl(AF_INET);
+ /* translate address family */
+ af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET;
+ POKE_U32(buf, af);
#endif
-
return (buf);
}
#endif /* SSH_TUN_FILTER */