mirror of
https://github.com/systemd/systemd.git
synced 2024-11-01 17:51:22 +03:00
sd-ndisc: better validate RA packets
Verify the hoplimit and that the received packet is large enough for the RA header. See <http://tools.ietf.org/html/rfc4861#section-6.1.2>.
This commit is contained in:
parent
6d06ac1faa
commit
cddf4d81ea
@ -47,17 +47,15 @@ int icmp6_bind_router_solicitation(int index) {
|
|||||||
.ipv6mr_interface = index,
|
.ipv6mr_interface = index,
|
||||||
};
|
};
|
||||||
_cleanup_close_ int s = -1;
|
_cleanup_close_ int s = -1;
|
||||||
int r, zero = 0, hops = 255;
|
int r, zero = 0, one = 1, hops = 255;
|
||||||
|
|
||||||
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK,
|
s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6);
|
||||||
IPPROTO_ICMPV6);
|
|
||||||
if (s < 0)
|
if (s < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
ICMP6_FILTER_SETBLOCKALL(&filter);
|
ICMP6_FILTER_SETBLOCKALL(&filter);
|
||||||
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
|
||||||
r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter,
|
r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter));
|
||||||
sizeof(filter));
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
@ -65,23 +63,23 @@ int icmp6_bind_router_solicitation(int index) {
|
|||||||
IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
|
IPV6_PKTINFO socket option also applies for ICMPv6 multicast.
|
||||||
Empirical experiments indicates otherwise and therefore an
|
Empirical experiments indicates otherwise and therefore an
|
||||||
IPV6_MULTICAST_IF socket option is used here instead */
|
IPV6_MULTICAST_IF socket option is used here instead */
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index,
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index));
|
||||||
sizeof(index));
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero,
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero));
|
||||||
sizeof(zero));
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops,
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops));
|
||||||
sizeof(hops));
|
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq,
|
r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
|
||||||
sizeof(mreq));
|
if (r < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one));
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return -errno;
|
return -errno;
|
||||||
|
|
||||||
|
@ -418,8 +418,7 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
|
static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) {
|
||||||
ssize_t len) {
|
|
||||||
void *opt;
|
void *opt;
|
||||||
struct nd_opt_hdr *opt_hdr;
|
struct nd_opt_hdr *opt_hdr;
|
||||||
|
|
||||||
@ -482,12 +481,25 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra,
|
|||||||
static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||||
_cleanup_free_ struct nd_router_advert *ra = NULL;
|
_cleanup_free_ struct nd_router_advert *ra = NULL;
|
||||||
sd_ndisc *nd = userdata;
|
sd_ndisc *nd = userdata;
|
||||||
int r, buflen = 0, pref, stateful;
|
union {
|
||||||
union sockaddr_union router = {};
|
struct cmsghdr cmsghdr;
|
||||||
socklen_t router_len = sizeof(router);
|
uint8_t buf[CMSG_LEN(sizeof(int))];
|
||||||
|
} control = {};
|
||||||
|
struct iovec iov = {};
|
||||||
|
union sockaddr_union sa = {};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = &sa.sa,
|
||||||
|
.msg_namelen = sizeof(sa),
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = &control,
|
||||||
|
.msg_controllen = sizeof(control),
|
||||||
|
};
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
struct in6_addr *gw;
|
struct in6_addr *gw;
|
||||||
unsigned lifetime;
|
unsigned lifetime;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
int r, pref, stateful, buflen = 0;
|
||||||
|
|
||||||
assert(s);
|
assert(s);
|
||||||
assert(nd);
|
assert(nd);
|
||||||
@ -500,24 +512,48 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
|
|||||||
/* This really should not happen */
|
/* This really should not happen */
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
ra = malloc(buflen);
|
iov.iov_len = buflen;
|
||||||
|
|
||||||
|
ra = malloc(iov.iov_len);
|
||||||
if (!ra)
|
if (!ra)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len);
|
iov.iov_base = ra;
|
||||||
|
|
||||||
|
len = recvmsg(fd, &msg, 0);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
if (errno == EAGAIN || errno == EINTR)
|
if (errno == EAGAIN || errno == EINTR)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
|
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
|
||||||
return -errno;
|
return -errno;
|
||||||
} else if (router_len == 0)
|
} else if ((size_t)len < sizeof(struct nd_router_advert)) {
|
||||||
|
return 0;
|
||||||
|
} else if (msg.msg_namelen == 0)
|
||||||
gw = NULL; /* only happens when running the test-suite over a socketpair */
|
gw = NULL; /* only happens when running the test-suite over a socketpair */
|
||||||
else if (router_len != sizeof(router.in6)) {
|
else if (msg.msg_namelen != sizeof(sa.in6)) {
|
||||||
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
|
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen);
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
gw = &router.in6.sin6_addr;
|
gw = &sa.in6.sin6_addr;
|
||||||
|
|
||||||
|
assert(!(msg.msg_flags & MSG_CTRUNC));
|
||||||
|
assert(!(msg.msg_flags & MSG_TRUNC));
|
||||||
|
|
||||||
|
CMSG_FOREACH(cmsg, &msg) {
|
||||||
|
if (cmsg->cmsg_level == SOL_IPV6 &&
|
||||||
|
cmsg->cmsg_type == IPV6_HOPLIMIT &&
|
||||||
|
cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
|
||||||
|
int hops = *(int*)CMSG_DATA(cmsg);
|
||||||
|
|
||||||
|
if (hops != 255) {
|
||||||
|
log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
|
if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
|
||||||
_cleanup_free_ char *addr = NULL;
|
_cleanup_free_ char *addr = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user