Import dhcpcd-7.2.1 with the following changes:

*  auth: Use consttime_memequal to avoid latency attack
*  DHCP: Fix a potential 1 byte read overflow with DHO_OPTSOVERLOADED
*  DHCPv6: Fix a potential buffer overflow reading NA/TA addresses
This commit is contained in:
roy 2019-04-26 14:32:27 +00:00
parent dbd4cee32e
commit dc127b4bb4
14 changed files with 188 additions and 70 deletions

View File

@ -117,7 +117,11 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
m = vm;
data = vdata;
/* Ensure that d is inside m which *may* not be the case for DHPCPv4 */
/* Ensure that d is inside m which *may* not be the case for DHCPv4.
* This can occur if the authentication option is split using
* DHCP long option from RFC 3399. Section 9 which does infact note that
* implementations should take this into account.
* Fixing this would be problematic, patches welcome. */
if (data < m || data > m + mlen || data + dlen > m + mlen) {
errno = ERANGE;
return NULL;
@ -354,7 +358,7 @@ gottoken:
}
free(mm);
if (memcmp(d, &hmac_code, dlen)) {
if (!consttime_memequal(d, &hmac_code, dlen)) {
errno = EPERM;
return NULL;
}

View File

@ -318,7 +318,7 @@ control_open(const char *ifname)
if ((fd = make_sock(&sa, ifname, 0)) != -1) {
socklen_t len;
len = (socklen_t)SUN_LEN(&sa);
if (connect(fd, (struct sockaddr *)&sa, len) == -1) {
close(fd);

View File

@ -28,7 +28,7 @@
#define CONFIG_H
#define PACKAGE "dhcpcd"
#define VERSION "7.2.0"
#define VERSION "7.2.1"
#ifndef CONFIG
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"

View File

@ -215,6 +215,12 @@ get_option(struct dhcpcd_ctx *ctx,
}
l = *p++;
/* Check we can read the option data, if present */
if (p + l > e) {
errno = EINVAL;
return NULL;
}
if (o == DHO_OPTSOVERLOADED) {
/* Ensure we only get this option once by setting
* the last bit as well as the value.
@ -249,10 +255,6 @@ get_option(struct dhcpcd_ctx *ctx,
bp += ol;
}
ol = l;
if (p + ol >= e) {
errno = EINVAL;
return NULL;
}
op = p;
bl += ol;
}
@ -2075,7 +2077,7 @@ dhcp_arp_probed(struct arp_state *astate)
ifp->name, inet_ntoa(astate->addr));
if (!(ifo->options & DHCPCD_INFORM))
dhcp_bind(ifp);
#ifndef IN_IFF_TENTATIVE
#ifndef IN_IFF_DUPLICATED
else {
struct bootp *bootp;
size_t len;
@ -2429,7 +2431,7 @@ dhcp_arp_address(struct interface *ifp)
if (astate == NULL)
return -1;
#ifdef IN_IFF_TENTATIVE
#ifdef IN_IFF_NOTUSEABLE
if (ia == NULL || ia->addr_flags & IN_IFF_NOTUSEABLE) {
state->state = DHS_PROBE;
if (ia == NULL) {

View File

@ -2029,12 +2029,12 @@ dhcp6_findna(struct interface *ifp, uint16_t ot, const uint8_t *iaid,
nd = o + ol;
l -= (size_t)(nd - d);
d = nd;
if (ol < 24) {
if (ol < sizeof(ia)) {
errno = EINVAL;
logerrx("%s: IA Address option truncated", ifp->name);
continue;
}
memcpy(&ia, o, ol);
memcpy(&ia, o, sizeof(ia));
ia.pltime = ntohl(ia.pltime);
ia.vltime = ntohl(ia.vltime);
/* RFC 3315 22.6 */
@ -3035,7 +3035,7 @@ dhcp6_bind(struct interface *ifp, const char *op, const char *sfrom)
* unless those values in those fields are 0.
*/
logwarnx("%s: ignoring T1 %"PRIu32
" to due address expiry",
" due to address expiry",
ifp->name, state->renew);
state->renew = state->rebind = 0;
}

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd September 15, 2018
.Dd April 24, 2019
.Dt DHCPCD.CONF 5
.Os
.Sh NAME
@ -376,8 +376,7 @@ IPv6 address configuration for the internal network.
noipv6rs # disable routing solicitation
denyinterfaces eth2 # Don't touch eth2 at all
interface eth0
ipv6rs # enable routing solicitation get the
# default IPv6 route
ipv6rs # enable routing solicitation for eth0
ia_na 1 # request an IPv6 address
ia_pd 2 eth1/0 # request a PD and assign it to eth1
ia_pd 3 eth2/1 eth3/2 # req a PD and assign it to eth2 and eth3

View File

@ -180,7 +180,9 @@ struct dhcpcd_ctx {
uint8_t *secret;
size_t secret_len;
#ifndef __sun
int nd_fd;
#endif
struct ra_head *ra_routers;
int dhcp6_fd;

View File

@ -943,10 +943,15 @@ if_getlifetime6(struct ipv6_addr *ia)
priv = (struct priv *)ia->iface->ctx->priv;
if (ioctl(priv->pf_inet6_fd, SIOCGIFALIFETIME_IN6, &ifr6) == -1)
return -1;
clock_gettime(CLOCK_MONOTONIC, &ia->created);
#if defined(__FreeBSD__) || defined(__DragonFly__)
t = ia->created.tv_sec;
#else
t = time(NULL);
lifetime = &ifr6.ifr_ifru.ifru_lifetime;
#endif
lifetime = &ifr6.ifr_ifru.ifru_lifetime;
if (lifetime->ia6t_preferred)
ia->prefix_pltime = (uint32_t)(lifetime->ia6t_preferred -
MIN(t, lifetime->ia6t_preferred));
@ -956,7 +961,6 @@ if_getlifetime6(struct ipv6_addr *ia)
ia->prefix_vltime = (uint32_t)(lifetime->ia6t_expire -
MIN(t, lifetime->ia6t_expire));
/* Calculate the created time */
clock_gettime(CLOCK_MONOTONIC, &ia->created);
ia->created.tv_sec -= lifetime->ia6t_vltime - ia->prefix_vltime;
} else
ia->prefix_vltime = ND6_INFINITE_LIFETIME;

View File

@ -62,9 +62,8 @@
* While it supports DaD, to seems to only expose IFF_DUPLICATE
* so we have no way of knowing if it's tentative or not.
* I don't even know if Solaris has any special treatment for tentative. */
# define IN_IFF_TENTATIVE 0
# define IN_IFF_DUPLICATED 0x02
# define IN_IFF_DETACHED 0
# define IN_IFF_NOTUSEABLE IN_IFF_DUPLICATED
#endif
#ifdef IN_IFF_TENTATIVE

View File

@ -232,7 +232,7 @@ static void
ipv4ll_probe(void *arg)
{
#ifdef IN_IFF_TENTATIVE
#ifdef IN_IFF_DUPLICATED
ipv4ll_probed(arg);
#else
arp_probe(arg);
@ -404,7 +404,7 @@ ipv4ll_start(void *arg)
if (ia == NULL)
ia = ipv4_iffindlladdr(ifp);
#ifdef IN_IFF_TENTATIVE
#ifdef IN_IFF_DUPLICATED
if (ia != NULL && ia->addr_flags & IN_IFF_DUPLICATED) {
ipv4_deladdr(ia, 0);
ia = NULL;
@ -419,6 +419,8 @@ ipv4ll_start(void *arg)
ifp->name, inet_ntoa(ia->addr));
return;
}
#endif
#ifdef IN_IFF_DUPLICATED
loginfox("%s: using IPv4LL address %s", ifp->name, ia->saddr);
#endif
ipv4ll_probed(astate);
@ -429,7 +431,7 @@ ipv4ll_start(void *arg)
if (state->pickedaddr.s_addr == INADDR_ANY)
state->pickedaddr.s_addr = ipv4ll_pickaddr(astate);
astate->addr = state->pickedaddr;
#ifdef IN_IFF_TENTATIVE
#ifdef IN_IFF_DUPLICATED
ipv4ll_probed(astate);
#else
arp_probe(astate);

View File

@ -137,7 +137,9 @@ ipv6_init(struct dhcpcd_ctx *ctx)
return -1;
TAILQ_INIT(ctx->ra_routers);
#ifndef __sun
ctx->nd_fd = -1;
#endif
ctx->dhcp6_fd = -1;
return 0;
}

View File

@ -44,9 +44,6 @@
# endif
#endif
#define ALLNODES "ff02::1"
#define ALLROUTERS "ff02::2"
#define EUI64_GBIT 0x01
#define EUI64_UBIT 0x02
#define EUI64_TO_IFID(in6) do {(in6)->s6_addr[8] ^= EUI64_UBIT; } while (0)
@ -77,6 +74,17 @@
(((d)->s6_addr32[3] ^ (a)->s6_addr32[3]) & (m)->s6_addr32[3]) == 0 )
#endif
#ifndef IN6ADDR_LINKLOCAL_ALLNODES_INIT
#define IN6ADDR_LINKLOCAL_ALLNODES_INIT \
{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 }}}
#endif
#ifndef IN6ADDR_LINKLOCAL_ALLROUTERS_INIT
#define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
{{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 }}}
#endif
/*
* BSD kernels don't inform userland of DAD results.
* See the discussion here:

View File

@ -190,54 +190,106 @@ ipv6nd_printoptions(const struct dhcpcd_ctx *ctx,
}
static int
ipv6nd_open(struct dhcpcd_ctx *ctx)
ipv6nd_open0(void)
{
int on;
int s, on;
struct icmp6_filter filt;
if (ctx->nd_fd != -1)
return ctx->nd_fd;
#define SOCK_FLAGS SOCK_CLOEXEC | SOCK_NONBLOCK
ctx->nd_fd = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
s = xsocket(PF_INET6, SOCK_RAW | SOCK_FLAGS, IPPROTO_ICMPV6);
#undef SOCK_FLAGS
if (ctx->nd_fd == -1)
if (s == -1)
return -1;
/* RFC4861 4.1 */
on = 255;
if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
&on, sizeof(on)) == -1)
goto eexit;
on = 1;
if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&on, sizeof(on)) == -1)
goto eexit;
on = 1;
if (setsockopt(ctx->nd_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
&on, sizeof(on)) == -1)
goto eexit;
ICMP6_FILTER_SETBLOCKALL(&filt);
ICMP6_FILTER_SETPASS(ND_NEIGHBOR_ADVERT, &filt);
ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
if (setsockopt(ctx->nd_fd, IPPROTO_ICMPV6, ICMP6_FILTER,
if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER,
&filt, sizeof(filt)) == -1)
goto eexit;
eloop_event_add(ctx->eloop, ctx->nd_fd, ipv6nd_handledata, ctx);
return ctx->nd_fd;
return s;
eexit:
if (ctx->nd_fd != -1) {
eloop_event_delete(ctx->eloop, ctx->nd_fd);
close(ctx->nd_fd);
ctx->nd_fd = -1;
}
close(s);
return -1;
}
#ifdef __sun
static int
ipv6nd_open(struct interface *ifp)
{
int s;
struct ipv6_mreq mreq = {
.ipv6mr_multiaddr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
.ipv6mr_interface = ifp->index
};
struct rs_state *state = RS_STATE(ifp);
uint_t ifindex = ifp->index;
if (state->nd_fd != -1)
return state->nd_fd;
s = ipv6nd_open0();
if (s == -1)
return -1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_BOUND_IF,
&ifindex, sizeof(ifindex)) == -1)
{
close(s);
return -1;
}
if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
&mreq, sizeof(mreq)) == -1)
{
close(s);
return -1;
}
state->nd_fd = s;
eloop_event_add(ifp->ctx->eloop, s, ipv6nd_handledata, ifp);
return s;
}
#else
static int
ipv6nd_open(struct dhcpcd_ctx *ctx)
{
int s, on;
if (ctx->nd_fd != -1)
return ctx->nd_fd;
s = ipv6nd_open0();
if (s == -1)
return -1;
on = 1;
if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
&on, sizeof(on)) == -1)
{
close(s);
return -1;
}
ctx->nd_fd = s;
eloop_event_add(ctx->eloop, s, ipv6nd_handledata, ctx);
return s;
}
#endif
static int
ipv6nd_makersprobe(struct interface *ifp)
{
@ -273,9 +325,12 @@ static void
ipv6nd_sendrsprobe(void *arg)
{
struct interface *ifp = arg;
struct dhcpcd_ctx *ctx;
struct rs_state *state = RS_STATE(ifp);
struct sockaddr_in6 dst = { .sin6_family = AF_INET6 };
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
.sin6_scope_id = ifp->index,
};
struct iovec iov = { .iov_base = state->rs, .iov_len = state->rslen };
unsigned char ctl[CMSG_SPACE(sizeof(struct in6_pktinfo))] = { 0 };
struct msghdr msg = {
@ -285,6 +340,7 @@ ipv6nd_sendrsprobe(void *arg)
};
struct cmsghdr *cm;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
int s;
if (ipv6_linklocal(ifp) == NULL) {
logdebugx("%s: delaying Router Solicitation for LL address",
@ -296,13 +352,6 @@ ipv6nd_sendrsprobe(void *arg)
#ifdef HAVE_SA_LEN
dst.sin6_len = sizeof(dst);
#endif
dst.sin6_scope_id = ifp->index;
if (inet_pton(AF_INET6, ALLROUTERS, &dst.sin6_addr) != 1) {
logerr(__func__);
return;
}
ctx = ifp->ctx;
/* Set the outbound interface */
cm = CMSG_FIRSTHDR(&msg);
@ -314,7 +363,12 @@ ipv6nd_sendrsprobe(void *arg)
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
logdebugx("%s: sending Router Solicitation", ifp->name);
if (sendmsg(ctx->nd_fd, &msg, 0) == -1) {
#ifdef __sun
s = state->nd_fd;
#else
s = ifp->ctx->nd_fd;
#endif
if (sendmsg(s, &msg, 0) == -1) {
logerr(__func__);
/* Allow IPv6ND to continue .... at most a few errors
* would be logged.
@ -342,6 +396,7 @@ ipv6nd_sendadvertisement(void *arg)
struct dhcpcd_ctx *ctx = ifp->ctx;
struct sockaddr_in6 dst = {
.sin6_family = AF_INET6,
.sin6_addr = IN6ADDR_LINKLOCAL_ALLNODES_INIT,
.sin6_scope_id = ifp->index,
};
struct iovec iov = { .iov_base = ia->na, .iov_len = ia->na_len };
@ -354,6 +409,7 @@ ipv6nd_sendadvertisement(void *arg)
struct cmsghdr *cm;
struct in6_pktinfo pi = { .ipi6_ifindex = ifp->index };
const struct rs_state *state = RS_CSTATE(ifp);
int s;
if (state == NULL || ifp->carrier <= LINK_DOWN)
goto freeit;
@ -361,10 +417,6 @@ ipv6nd_sendadvertisement(void *arg)
#ifdef SIN6_LEN
dst.sin6_len = sizeof(dst);
#endif
if (inet_pton(AF_INET6, ALLNODES, &dst.sin6_addr) != 1) {
logerr(__func__);
return;
}
/* Set the outbound interface. */
cm = CMSG_FIRSTHDR(&msg);
@ -373,10 +425,20 @@ ipv6nd_sendadvertisement(void *arg)
cm->cmsg_type = IPV6_PKTINFO;
cm->cmsg_len = CMSG_LEN(sizeof(pi));
memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
logdebugx("%s: sending NA for %s", ifp->name, ia->saddr);
if (sendmsg(ctx->nd_fd, &msg, 0) == -1)
#ifdef __sun
s = state->nd_fd;
#else
s = ctx->nd_fd;
#endif
if (sendmsg(s, &msg, 0) == -1)
#ifdef __OpenBSD__
/* This isn't too critical as they don't support IPv6 address sharing */
#warning Cannot send NA messages on OpenBSD
logdebug(__func__);
#else
logerr(__func__);
#endif
if (++ia->na_count < MAX_NEIGHBOR_ADVERTISEMENT) {
eloop_timeout_add_sec(ctx->eloop,
@ -619,6 +681,11 @@ ipv6nd_free(struct interface *ifp)
if (state == NULL)
return 0;
ctx = ifp->ctx;
#ifdef __sun
eloop_event_delete(ctx->eloop, state->nd_fd);
close(state->nd_fd);
#endif
free(state->rs);
free(state);
ifp->if_data[IF_DATA_IPV6ND] = NULL;
@ -630,9 +697,9 @@ ipv6nd_free(struct interface *ifp)
}
}
#ifndef __sun
/* If we don't have any more IPv6 enabled interfaces,
* close the global socket and release resources */
ctx = ifp->ctx;
TAILQ_FOREACH(ifp, ctx->ifaces, next) {
if (RS_STATE(ifp))
break;
@ -644,6 +711,7 @@ ipv6nd_free(struct interface *ifp)
ctx->nd_fd = -1;
}
}
#endif
return n;
}
@ -1659,6 +1727,7 @@ static void
ipv6nd_handledata(void *arg)
{
struct dhcpcd_ctx *ctx;
int s;
struct sockaddr_in6 from;
unsigned char buf[64 * 1024]; /* Maximum ICMPv6 size */
struct iovec iov = {
@ -1677,8 +1746,18 @@ ipv6nd_handledata(void *arg)
struct icmp6_hdr *icp;
struct interface *ifp;
#ifdef __sun
struct rs_state *state;
ifp = arg;
state = RS_STATE(ifp);
ctx = ifp->ctx;
s = state->nd_fd;
#else
ctx = arg;
len = recvmsg(ctx->nd_fd, &msg, 0);
s = ctx->nd_fd;
#endif
len = recvmsg(s, &msg, 0);
if (len == -1) {
logerr(__func__);
return;
@ -1689,11 +1768,15 @@ ipv6nd_handledata(void *arg)
return;
}
#ifdef __sun
if_findifpfromcmsg(ctx, &msg, &hoplimit);
#else
ifp = if_findifpfromcmsg(ctx, &msg, &hoplimit);
if (ifp == NULL) {
logerr(__func__);
return;
}
#endif
/* Don't do anything if the user hasn't configured it. */
if (ifp->active != IF_ACTIVE_USER ||
@ -1725,11 +1808,6 @@ ipv6nd_startrs1(void *arg)
struct rs_state *state;
loginfox("%s: soliciting an IPv6 router", ifp->name);
if (ipv6nd_open(ifp->ctx) == -1) {
logerr(__func__);
return;
}
state = RS_STATE(ifp);
if (state == NULL) {
ifp->if_data[IF_DATA_IPV6ND] = calloc(1, sizeof(*state));
@ -1738,8 +1816,23 @@ ipv6nd_startrs1(void *arg)
logerr(__func__);
return;
}
#ifdef __sun
state->nd_fd = -1;
#endif
}
#ifdef __sun
if (ipv6nd_open(ifp) == -1) {
logerr(__func__);
return;
}
#else
if (ipv6nd_open(ifp->ctx) == -1) {
logerr(__func__);
return;
}
#endif
/* Always make a new probe as the underlying hardware
* address could have changed. */
ipv6nd_makersprobe(ifp);

View File

@ -61,6 +61,9 @@ struct rs_state {
size_t rslen;
int rsprobes;
uint32_t retrans;
#ifdef __sun
int nd_fd;
#endif
};
#define RS_STATE(a) ((struct rs_state *)(ifp)->if_data[IF_DATA_IPV6ND])