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@
This commit is contained in:
Damien Miller 2017-07-21 14:38:16 +10:00
parent 2985d4062e
commit c78e6eec78

View File

@ -199,49 +199,50 @@ sys_tun_open(int tun, int mode)
*/ */
#if defined(SSH_TUN_FILTER) #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_INET 2
#define OPENBSD_AF_INET6 24 #define OPENBSD_AF_INET6 24
int 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) #if defined(SSH_TUN_PREPEND_AF)
char rbuf[CHAN_RBUF]; char rbuf[CHAN_RBUF];
struct ip *iph; struct ip iph;
#endif #endif
u_int32_t *af; #if defined(SSH_TUN_PREPEND_AF) || defined(SSH_TUN_COMPAT_AF)
char *ptr = buf; u_int32_t af;
int r; #endif
/* XXX update channel input filter API to use unsigned length */
if (_len < 0)
return -1;
len = _len;
#if defined(SSH_TUN_PREPEND_AF) #if defined(SSH_TUN_PREPEND_AF)
if (len <= 0 || len > (int)(sizeof(rbuf) - sizeof(*af))) if (len <= sizeof(iph) || len > sizeof(rbuf) - 4)
return (-1); return -1;
ptr = (char *)&rbuf[0]; /* Determine address family from packet IP header. */
bcopy(buf, ptr + sizeof(u_int32_t), len); memcpy(&iph, buf, sizeof(iph));
len += sizeof(u_int32_t); af = iph.ip_v == 6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET;
af = (u_int32_t *)ptr; /* Prepend address family to packet using OpenBSD constants */
memcpy(rbuf + 4, buf, len);
iph = (struct ip *)(ptr + sizeof(u_int32_t)); len += 4;
switch (iph->ip_v) { POKE_U32(rbuf, af);
case 6: ptr = rbuf;
*af = AF_INET6; #elif defined(SSH_TUN_COMPAT_AF)
break; /* Convert existing address family header to OpenBSD value */
case 4: if (len <= 4)
default: return -1;
*af = AF_INET; af = PEEK_U32(buf);
break; /* Put it back */
} POKE_U32(buf, af == AF_INET6 ? OPENBSD_AF_INET6 : OPENBSD_AF_INET);
#endif
#if defined(SSH_TUN_COMPAT_AF)
if (len < (int)sizeof(u_int32_t))
return (-1);
af = (u_int32_t *)ptr;
if (*af == htonl(AF_INET6))
*af = htonl(OPENBSD_AF_INET6);
else
*af = htonl(OPENBSD_AF_INET);
#endif #endif
if ((r = sshbuf_put_string(&c->input, ptr, len)) != 0) 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) sys_tun_outfilter(struct Channel *c, u_char **data, u_int *dlen)
{ {
u_char *buf; u_char *buf;
u_int32_t *af; u_int32_t af;
int r; int r;
size_t xxx_dlen; 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)); fatal("%s: buffer error: %s", __func__, ssh_err(r));
if (dlen != NULL) if (dlen != NULL)
*dlen = xxx_dlen; *dlen = xxx_dlen;
if (*dlen < sizeof(*af)) if (*dlen < sizeof(af))
return (NULL); return (NULL);
buf = *data; buf = *data;
#if defined(SSH_TUN_PREPEND_AF) #if defined(SSH_TUN_PREPEND_AF)
*dlen -= sizeof(u_int32_t); /* skip address family */
buf = *data + sizeof(u_int32_t); *dlen -= sizeof(af);
buf = *data + sizeof(af);
#elif defined(SSH_TUN_COMPAT_AF) #elif defined(SSH_TUN_COMPAT_AF)
af = ntohl(*(u_int32_t *)buf); /* translate address family */
if (*af == OPENBSD_AF_INET6) af = (PEEK_U32(buf) == OPENBSD_AF_INET6) ? AF_INET6 : AF_INET;
*af = htonl(AF_INET6); POKE_U32(buf, af);
else
*af = htonl(AF_INET);
#endif #endif
return (buf); return (buf);
} }
#endif /* SSH_TUN_FILTER */ #endif /* SSH_TUN_FILTER */