Import dhcpcd-7.0.4 with the following changes:

* Routing: Fix case when cloning route changes but needs to be replaced
 * DHCP6: Transpose DHCP userclass option into DHCP6
 * DHCP6: Fix sending custom vendor class option
 * Auth: Allow zero value replay detection data
 * Auth: Allow different tokens for send and receive
 * ND6: Warn if router lifetime is set to zero
 * DHCP6: Softwire Address and Port-Mapped Clients, RFC7598
This commit is contained in:
roy 2018-05-02 22:06:41 +00:00
parent 71c9531719
commit 483cca5a38
11 changed files with 181 additions and 63 deletions

View File

@ -151,7 +151,24 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
memcpy(&replay, d, sizeof(replay));
replay = ntohll(replay);
if (state->token) {
/*
* Test for a replay attack.
*
* NOTE: Some servers always send a replay data value of zero.
* This is strictly compliant with RFC 3315 and 3318 which say:
* "If the RDM field contains 0x00, the replay detection field MUST be
* set to the value of a monotonically increasing counter."
* An example of a monotonically increasing sequence is:
* 1, 2, 2, 2, 2, 2, 2
* Errata 3474 updates RFC 3318 to say:
* "If the RDM field contains 0x00, the replay detection field MUST be
* set to the value of a strictly increasing counter."
*
* Taking the above into account, dhcpcd will only test for
* strictly speaking replay attacks if it receives any non zero
* replay data to validate against.
*/
if (state->token && state->replay != 0) {
if (state->replay == (replay ^ 0x8000000000000000ULL)) {
/* We don't know if the singular point is increasing
* or decreasing. */
@ -174,7 +191,7 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
* Rest of data is MAC. */
switch (protocol) {
case AUTH_PROTO_TOKEN:
secretid = 0;
secretid = auth->token_rcv_secretid;
break;
case AUTH_PROTO_DELAYED:
if (dlen < sizeof(secretid) + sizeof(hmac_code)) {
@ -182,6 +199,7 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
return NULL;
}
memcpy(&secretid, d, sizeof(secretid));
secretid = ntohl(secretid);
d += sizeof(secretid);
dlen -= sizeof(secretid);
break;
@ -197,6 +215,7 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
dlen -= realm_len;
}
memcpy(&secretid, d, sizeof(secretid));
secretid = ntohl(secretid);
d += sizeof(secretid);
dlen -= sizeof(secretid);
break;
@ -266,7 +285,6 @@ dhcp_auth_validate(struct authstate *state, const struct auth *auth,
}
/* Find a token for the realm and secret */
secretid = ntohl(secretid);
TAILQ_FOREACH(t, &auth->tokens, next) {
if (t->secretid == secretid &&
t->realm_len == realm_len &&
@ -478,14 +496,16 @@ dhcp_auth_encode(struct auth *auth, const struct token *t,
uint64_t rdm;
uint8_t hmac_code[HMAC_LENGTH];
time_t now;
uint8_t hops, *p, info, *m, *data;
uint8_t hops, *p, *m, *data;
uint32_t giaddr, secretid;
bool auth_info;
if (auth->protocol == 0 && t == NULL) {
/* Ignore the token argument given to us - always send using the
* configured token. */
if (auth->protocol == AUTH_PROTO_TOKEN) {
TAILQ_FOREACH(t, &auth->tokens, next) {
if (t->secretid == 0 &&
t->realm_len == 0)
break;
if (t->secretid == auth->token_snd_secretid)
break;
}
if (t == NULL) {
errno = EINVAL;
@ -532,9 +552,9 @@ dhcp_auth_encode(struct auth *auth, const struct token *t,
/* DISCOVER or INFORM messages don't write auth info */
if ((mp == 4 && (mt == DHCP_DISCOVER || mt == DHCP_INFORM)) ||
(mp == 6 && (mt == DHCP6_SOLICIT || mt == DHCP6_INFORMATION_REQ)))
info = 0;
auth_info = false;
else
info = 1;
auth_info = true;
/* Work out the auth area size.
* We only need to do this for DISCOVER messages */
@ -545,11 +565,11 @@ dhcp_auth_encode(struct auth *auth, const struct token *t,
dlen += t->key_len;
break;
case AUTH_PROTO_DELAYEDREALM:
if (info && t)
if (auth_info && t)
dlen += t->realm_len;
/* FALLTHROUGH */
case AUTH_PROTO_DELAYED:
if (info && t)
if (auth_info && t)
dlen += sizeof(t->secretid) + sizeof(hmac_code);
break;
}
@ -572,18 +592,32 @@ dhcp_auth_encode(struct auth *auth, const struct token *t,
/* Write out our option */
*data++ = auth->protocol;
*data++ = auth->algorithm;
*data++ = auth->rdm;
switch (auth->rdm) {
case AUTH_RDM_MONOTONIC:
rdm = get_next_rdm_monotonic(auth);
break;
default:
/* This block appeases gcc, clang doesn't need it */
rdm = get_next_rdm_monotonic(auth);
break;
/*
* RFC 3315 21.4.4.1 says that SOLICIT in DELAYED authentication
* should not set RDM or it's data.
* An expired draft draft-ietf-dhc-dhcpv6-clarify-auth-01 suggets
* this should not be set for INFORMATION REQ messages as well,
* which is probably a good idea because both states start from zero.
*/
if (auth_info ||
!(auth->protocol & (AUTH_PROTO_DELAYED | AUTH_PROTO_DELAYEDREALM)))
{
*data++ = auth->rdm;
switch (auth->rdm) {
case AUTH_RDM_MONOTONIC:
rdm = get_next_rdm_monotonic(auth);
break;
default:
/* This block appeases gcc, clang doesn't need it */
rdm = get_next_rdm_monotonic(auth);
break;
}
rdm = htonll(rdm);
memcpy(data, &rdm, 8);
} else {
*data++ = 0; /* rdm */
memset(data, 0, 8); /* replay detection data */
}
rdm = htonll(rdm);
memcpy(data, &rdm, 8);
data += 8;
dlen -= 1 + 1 + 1 + 8;
@ -603,7 +637,7 @@ dhcp_auth_encode(struct auth *auth, const struct token *t,
}
/* DISCOVER or INFORM messages don't write auth info */
if (!info)
if (!auth_info)
return (ssize_t)dlen;
/* Loading a saved lease without an authentication option */

View File

@ -71,6 +71,8 @@ struct auth {
uint64_t last_replay;
uint8_t last_replay_set;
struct token_head tokens;
uint32_t token_snd_secretid;
uint32_t token_rcv_secretid;
#endif
};

View File

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

View File

@ -193,6 +193,48 @@ dhcp6_printoptions(const struct dhcpcd_ctx *ctx,
}
}
static size_t
dhcp6_makeuser(void *data, const struct interface *ifp)
{
const struct if_options *ifo = ifp->options;
struct dhcp6_option o;
uint8_t *p;
const uint8_t *up, *ue;
uint16_t ulen, unlen;
size_t olen;
/* Convert the DHCPv4 user class option to DHCPv6 */
up = ifo->userclass;
ulen = *up++;
if (ulen == 0)
return 0;
p = data;
olen = 0;
if (p != NULL)
p += sizeof(o);
ue = up + ulen;
for (; up < ue; up += ulen) {
ulen = *up++;
olen += sizeof(ulen) + ulen;
if (data == NULL)
continue;
unlen = htons(ulen);
memcpy(p, &unlen, sizeof(unlen));
p += sizeof(unlen);
memcpy(p, up, ulen);
p += ulen;
}
if (data != NULL) {
o.code = htons(D6_OPTION_USER_CLASS);
o.len = htons((uint16_t)olen);
memcpy(data, &o, sizeof(o));
}
return sizeof(o) + olen;
}
static size_t
dhcp6_makevendor(void *data, const struct interface *ifp)
{
@ -245,7 +287,7 @@ dhcp6_makevendor(void *data, const struct interface *ifp)
{
hvlen = htons((uint16_t)vivco->len);
memcpy(p, &hvlen, sizeof(hvlen));
p += sizeof(len);
p += sizeof(hvlen);
memcpy(p, vivco->data, vivco->len);
p += vivco->len;
}
@ -677,6 +719,8 @@ dhcp6_makemessage(struct interface *ifp)
len += sizeof(*state->send);
len += sizeof(o) + ifp->ctx->duid_len;
len += sizeof(o) + sizeof(uint16_t); /* elapsed */
if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
len += dhcp6_makeuser(NULL, ifp);
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
len += dhcp6_makevendor(NULL, ifp);
@ -844,6 +888,8 @@ dhcp6_makemessage(struct interface *ifp)
si_len = 0;
COPYIN(D6_OPTION_ELAPSED, &si_len, sizeof(si_len));
if (!has_option_mask(ifo->nomask6, D6_OPTION_USER_CLASS))
p += dhcp6_makeuser(p, ifp);
if (!has_option_mask(ifo->nomask6, D6_OPTION_VENDOR_CLASS))
p += dhcp6_makevendor(p, ifp);
@ -3410,13 +3456,6 @@ dhcp6_recv(struct dhcpcd_ctx *ctx, struct ipv6_addr *ia)
bytes = recvmsg_realloc(s, &ctx->rcvhdr, 0);
if (bytes == -1) {
logerr("%s: recvmsg_realloc", __func__);
close(s);
eloop_event_delete(ctx->eloop, s);
if (ia != NULL)
ia->dhcp6_fd = -1;
else
ctx->dhcp6_fd = -1;
eloop_exit(ctx->eloop, 1);
return;
}
len = (size_t)bytes;

View File

@ -67,6 +67,7 @@
#define D6_OPTION_UNICAST 12
#define D6_OPTION_STATUS_CODE 13
#define D6_OPTION_RAPID_COMMIT 14
#define D6_OPTION_USER_CLASS 15
#define D6_OPTION_VENDOR_CLASS 16
#define D6_OPTION_VENDOR_OPTS 17
#define D6_OPTION_INTERFACE_ID 18

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd January 8, 2018
.Dd April 29, 2018
.Dt DHCPCD 8
.Os
.Sh NAME
@ -753,6 +753,17 @@ to rebind, reconfigure or exit need to include the same restrictive flags
so that
.Nm
knows which process to signal.
.Pp
Some DHCP servers implement ClientID filtering.
If
.Nm
is replacing an in-use DHCP client then you might need to adjust the clientid
option
.Nm
sends to match.
If using a DUID in place of the ClientID, edit
.Pa @DBDIR@/duid
accordingly.
.Sh FILES
.Bl -ohang
.It Pa @SYSCONFDIR@/dhcpcd.conf

View File

@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.Dd February 2, 2018
.Dd April 29, 2018
.Dt DHCPCD.CONF 5
.Os
.Sh NAME
@ -74,9 +74,16 @@ Example:
.D1 # A generic 192.168.0.1 network
.D1 profile 192.168.0.1
.D1 static ip_address=192.168.0.98/24
.It Ic authprotocol Ar protocol Ar algorithm Ar rdm
.It Ic authprotocol Ar protocol Op Ar algorithm Op Ar rdm
Authenticate DHCP messages.
See the Supported Authentication Protocols section.
If
.Ar protocol
is
.Ar token
then
.Ar algorithm is
snd_secretid/rcv_secretid so you can send and recieve different tokens.
.It Ic authtoken Ar secretid Ar realm Ar expire Ar key
Define a shared key for use in authentication.
.Ar realm
@ -863,7 +870,11 @@ References an option from the global definition.
.Ss Supported Authentication Protocols
.Bl -tag -width -indent
.It Ic token
Sends and expects the token with the secretid 0 and realm of "" in each message.
Sends a plain text token the server expects and matches a token sent by
the server.
The tokens to not have to be the same.
If unspecified, the token with secretid of 0 will be used in sending messages
and validating received messages.
.It Ic delayedrealm
Delayed Authentication.
.Nm dhcpcd

View File

@ -496,13 +496,12 @@ if_route(unsigned char cmd, const struct rt *rt)
rtm->rtm_flags |= RTF_UP;
rtm->rtm_addrs |= RTA_GATEWAY;
#ifdef RTA_LABEL
rtm->rtm_addrs |= RTA_LABEL;
#endif
if (!(rtm->rtm_flags & RTF_REJECT) &&
!sa_is_loopback(&rt->rt_gateway))
{
rtm->rtm_addrs |= RTA_IFP;
rtm->rtm_index = (unsigned short)rt->rt_ifp->index;
if (!gateway_unspec)
rtm->rtm_addrs |= RTA_IFP;
if (!sa_is_unspecified(&rt->rt_ifa))
rtm->rtm_addrs |= RTA_IFA;
}
@ -574,10 +573,8 @@ if_route(unsigned char cmd, const struct rt *rt)
if (rtm->rtm_addrs & RTA_NETMASK)
ADDSA(&rt->rt_netmask);
if (rtm->rtm_addrs & RTA_IFP) {
rtm->rtm_index = (unsigned short)rt->rt_ifp->index;
if (rtm->rtm_addrs & RTA_IFP)
ADDSA((struct sockaddr *)&sdl);
}
if (rtm->rtm_addrs & RTA_IFA)
ADDSA(&rt->rt_ifa);

View File

@ -1850,6 +1850,7 @@ err_sla:
logerrx("invalid code: %s", arg);
return -1;
}
fp = strskipwhite(fp);
if (fp) {
s = parse_string(NULL, 0, fp);
if (s == -1) {
@ -1912,12 +1913,32 @@ err_sla:
}
if (fp)
*fp++ = '\0';
if (strcasecmp(arg, "hmacmd5") == 0 ||
strcasecmp(arg, "hmac-md5") == 0)
ifo->auth.algorithm = AUTH_ALG_HMAC_MD5;
else {
logerrx("%s: unsupported algorithm", arg);
return 1;
if (ifo->auth.protocol == AUTH_PROTO_TOKEN) {
np = strchr(arg, '/');
if (np) {
if (fp == NULL || np < fp)
*np++ = '\0';
else
np = NULL;
}
if (parse_uint32(&ifo->auth.token_snd_secretid,
arg) == -1)
logerrx("%s: not a number", arg);
else
ifo->auth.token_rcv_secretid =
ifo->auth.token_snd_secretid;
if (np &&
parse_uint32(&ifo->auth.token_rcv_secretid,
np) == -1)
logerrx("%s: not a number", arg);
} else {
if (strcasecmp(arg, "hmacmd5") == 0 ||
strcasecmp(arg, "hmac-md5") == 0)
ifo->auth.algorithm = AUTH_ALG_HMAC_MD5;
else {
logerrx("%s: unsupported algorithm", arg);
return 1;
}
}
arg = fp;
if (arg == NULL) {

View File

@ -749,7 +749,8 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
struct in6_addr pi_prefix;
struct ipv6_addr *ap;
struct dhcp_opt *dho;
uint8_t new_rap, new_data;
bool new_rap, new_data;
uint32_t old_lifetime;
__printflike(1, 2) void (*logfunc)(const char *, ...);
#ifdef IPV6_MANAGETEMPADDR
uint8_t new_ap;
@ -764,25 +765,25 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
}
if (len < sizeof(struct nd_router_advert)) {
logerr("IPv6 RA packet too short from %s", ctx->sfrom);
logerrx("IPv6 RA packet too short from %s", ctx->sfrom);
return;
}
/* RFC 4861 7.1.2 */
if (hoplimit != 255) {
logerr("invalid hoplimit(%d) in RA from %s",
logerrx("invalid hoplimit(%d) in RA from %s",
hoplimit, ctx->sfrom);
return;
}
if (!IN6_IS_ADDR_LINKLOCAL(&ctx->from.sin6_addr)) {
logerr("RA from non local address %s", ctx->sfrom);
logerrx("RA from non local address %s", ctx->sfrom);
return;
}
if (!(ifp->options->options & DHCPCD_IPV6RS)) {
#ifdef DEBUG_RS
logerr("%s: unexpected RA from %s",
logerrx("%s: unexpected RA from %s",
ifp->name, ctx->sfrom);
#endif
return;
@ -820,9 +821,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
free(rap->data);
rap->data_len = 0;
}
new_data = 1;
new_data = true;
} else
new_data = 0;
new_data = false;
if (rap == NULL) {
rap = calloc(1, sizeof(*rap));
if (rap == NULL) {
@ -833,9 +834,9 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
rap->from = ctx->from.sin6_addr;
strlcpy(rap->sfrom, ctx->sfrom, sizeof(rap->sfrom));
TAILQ_INIT(&rap->addrs);
new_rap = 1;
new_rap = true;
} else
new_rap = 0;
new_rap = false;
if (rap->data_len == 0) {
rap->data = malloc(len);
if (rap->data == NULL) {
@ -858,7 +859,11 @@ ipv6nd_handlera(struct dhcpcd_ctx *ctx, struct interface *ifp,
clock_gettime(CLOCK_MONOTONIC, &rap->acquired);
rap->flags = nd_ra->nd_ra_flags_reserved;
old_lifetime = rap->lifetime;
rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
if (!new_rap && rap->lifetime == 0 && old_lifetime != 0)
logwarnx("%s: %s: no longer a default router",
ifp->name, rap->sfrom);
if (nd_ra->nd_ra_reachable) {
rap->reachable = ntohl(nd_ra->nd_ra_reachable);
if (rap->reachable > MAX_REACHABLE_TIME)
@ -1529,9 +1534,6 @@ ipv6nd_handledata(void *arg)
len = recvmsg_realloc(ctx->nd_fd, &ctx->rcvhdr, 0);
if (len == -1) {
logerr(__func__);
eloop_event_delete(ctx->eloop, ctx->nd_fd);
close(ctx->nd_fd);
ctx->nd_fd = -1;
return;
}
ctx->sfrom = inet_ntop(AF_INET6, &ctx->from.sin6_addr,

View File

@ -359,7 +359,7 @@ rt_add(struct rt *nrt, struct rt *ort)
* As such, we need to delete and re-add the route to flush children
* to correct the flags. */
if (change && ort != NULL && ort->rt_flags & RTF_CLONING)
change = true;
change = false;
#endif
if (change) {