Import dhcpcd-5.1.12 with the following changes:

* 20-resolv.conf now uses the correct variable for $IF_METRIC
* Exclude interface values when dumping the lease
* Parse static value subnet_mask when it exists instead of deriving from
  ip address
* logger calls now resemble dhcpcd calls to syslog(3)
* Reject offered IP address if INADDR_BROADCAST or INADDR_ANY
* Change the route if source address has changed
* Note the address we are requesting in the broadcast log entry
* When operating on one interface, respect the timeout for in dhcpcd.conf
* Escape | and & characters before passing the value to the shell
  Ensure we set a valid hostname, DNS domain and NIS domain.
  Document the need for input validation in dhcpcd-run-hooks(8).
  Fixes CVE-2011-996
  Based on a patch to dhcpcd-3 by Marius Tomaschewski <mt@suse.de>
This commit is contained in:
roy 2011-04-06 09:06:41 +00:00
parent e21f0b912e
commit a2af890d85
17 changed files with 162 additions and 48 deletions

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2008 Roy Marples <roy@marples.name> * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -119,7 +119,7 @@ handle_arp_packet(void *arg)
state->fail.s_addr = 0; state->fail.s_addr = 0;
for(;;) { for(;;) {
bytes = get_raw_packet(iface, ETHERTYPE_ARP, bytes = get_raw_packet(iface, ETHERTYPE_ARP,
arp_buffer, sizeof(arp_buffer)); arp_buffer, sizeof(arp_buffer), NULL);
if (bytes == 0 || bytes == -1) if (bytes == 0 || bytes == -1)
return; return;
/* We must have a full ARP header */ /* We must have a full ARP header */

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2008 Roy Marples <roy@marples.name> * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
@ -160,7 +160,7 @@ send_raw_packet(const struct interface *iface, int protocol,
* So we pass the buffer in the API so we can loop on >1 packet. */ * So we pass the buffer in the API so we can loop on >1 packet. */
ssize_t ssize_t
get_raw_packet(struct interface *iface, int protocol, get_raw_packet(struct interface *iface, int protocol,
void *data, ssize_t len) void *data, ssize_t len, int *partialcsum)
{ {
int fd = -1; int fd = -1;
struct bpf_hdr packet; struct bpf_hdr packet;
@ -172,6 +172,9 @@ get_raw_packet(struct interface *iface, int protocol,
else else
fd = iface->raw_fd; fd = iface->raw_fd;
if (partialcsum != NULL)
*partialcsum = 0; /* Not supported on BSD */
for (;;) { for (;;) {
if (iface->buffer_len == 0) { if (iface->buffer_len == 0) {
bytes = read(fd, iface->buffer, iface->buffer_size); bytes = read(fd, iface->buffer, iface->buffer_size);

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2010 Roy Marples <roy@marples.name> * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -170,8 +170,14 @@ make_env(const struct interface *iface, char ***argv)
const struct if_options *ifo = iface->state->options; const struct if_options *ifo = iface->state->options;
const struct interface *ifp; const struct interface *ifp;
/* When dumping the lease, we only want to report interface and
reason - the other interface variables are meaningless */
if (options & DHCPCD_DUMPLEASE)
elen = 2;
else
elen = 8;
/* Make our env */ /* Make our env */
elen = 8;
env = xmalloc(sizeof(char *) * (elen + 1)); env = xmalloc(sizeof(char *) * (elen + 1));
e = strlen("interface") + strlen(iface->name) + 2; e = strlen("interface") + strlen(iface->name) + 2;
env[0] = xmalloc(e); env[0] = xmalloc(e);
@ -179,7 +185,10 @@ make_env(const struct interface *iface, char ***argv)
e = strlen("reason") + strlen(iface->state->reason) + 2; e = strlen("reason") + strlen(iface->state->reason) + 2;
env[1] = xmalloc(e); env[1] = xmalloc(e);
snprintf(env[1], e, "reason=%s", iface->state->reason); snprintf(env[1], e, "reason=%s", iface->state->reason);
e = 20; if (options & DHCPCD_DUMPLEASE)
goto dumplease;
e = 20;
env[2] = xmalloc(e); env[2] = xmalloc(e);
snprintf(env[2], e, "pid=%d", getpid()); snprintf(env[2], e, "pid=%d", getpid());
env[3] = xmalloc(e); env[3] = xmalloc(e);
@ -237,6 +246,8 @@ make_env(const struct interface *iface, char ***argv)
append_config(&env, &elen, "old", append_config(&env, &elen, "old",
(const char *const *)ifo->config); (const char *const *)ifo->config);
} }
dumplease:
if (iface->state->new) { if (iface->state->new) {
e = configure_env(NULL, NULL, iface->state->new, ifo); e = configure_env(NULL, NULL, iface->state->new, ifo);
if (e > 0) { if (e > 0) {
@ -653,9 +664,11 @@ build_routes(void)
/* Is this route already in our table? */ /* Is this route already in our table? */
if ((find_route(nrs, rt, NULL, NULL)) != NULL) if ((find_route(nrs, rt, NULL, NULL)) != NULL)
continue; continue;
rt->src.s_addr = ifp->addr.s_addr;
/* Do we already manage it? */ /* Do we already manage it? */
if ((or = find_route(routes, rt, &rtl, NULL))) { if ((or = find_route(routes, rt, &rtl, NULL))) {
if (or->iface != ifp || if (or->iface != ifp ||
or->src.s_addr != ifp->addr.s_addr ||
rt->gate.s_addr != or->gate.s_addr) rt->gate.s_addr != or->gate.s_addr)
{ {
if (c_route(or, rt, ifp) != 0) if (c_route(or, rt, ifp) != 0)

View File

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

View File

@ -1143,7 +1143,9 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data)
case '\'': /* FALLTHROUGH */ case '\'': /* FALLTHROUGH */
case '$': /* FALLTHROUGH */ case '$': /* FALLTHROUGH */
case '`': /* FALLTHROUGH */ case '`': /* FALLTHROUGH */
case '\\': case '\\': /* FALLTHROUGH */
case '|': /* FALLTHROUGH */
case '&':
if (s) { if (s) {
if (len < 3) { if (len < 3) {
errno = ENOBUFS; errno = ENOBUFS;

View File

@ -81,20 +81,28 @@ add_resolv_conf()
if [ -n "$new_domain_name" ]; then if [ -n "$new_domain_name" ]; then
set -- $new_domain_name set -- $new_domain_name
new_domain_name="$1" new_domain_name="$1"
conf="${conf}domain $new_domain_name\n" if valid_domainname "$new_domain_name"; then
conf="${conf}domain $new_domain_name\n"
else
syslog err "Invalid domain name: $new_domain_name"
fi
# Support RFC violating search in domain # Support RFC violating search in domain
if [ -z "$new_domain_search" -a -n "$2" ]; then if [ -z "$new_domain_search" -a -n "$2" ]; then
new_domain_search="$@" new_domain_search="$@"
fi fi
fi fi
if [ -n "$new_domain_search" ]; then if [ -n "$new_domain_search" ]; then
conf="${conf}search $new_domain_search\n" if valid_domainname_list; then
conf="${conf}search $new_domain_search\n"
else
syslog err "Invalid domain name in list: $new_domain_search"
fi
fi fi
for x in ${new_domain_name_servers}; do for x in ${new_domain_name_servers}; do
conf="${conf}nameserver $x\n" conf="${conf}nameserver $x\n"
done done
if type resolvconf >/dev/null 2>&1; then if type resolvconf >/dev/null 2>&1; then
[ -n "$metric" ] && export IF_METRIC="$metric" [ -n "$ifmetric" ] && export IF_METRIC="$ifmetric"
printf "$conf" | resolvconf -a "$interface" printf "$conf" | resolvconf -a "$interface"
return $? return $?
fi fi

View File

@ -18,13 +18,22 @@ need_hostname()
esac esac
} }
try_hostname()
{
if valid_domainname "$1"; then
hostname "$1"
else
syslog err "Invalid hostname: $1"
fi
}
set_hostname() set_hostname()
{ {
if need_hostname; then if need_hostname; then
if [ -n "$new_host_name" ]; then if [ -n "$new_host_name" ]; then
hostname "$new_host_name" try_hostname "$new_host_name"
elif [ -n "$new_fqdn_name" ]; then elif [ -n "$new_fqdn_name" ]; then
hostname "$new_fqdn_name" try_hostname "$new_fqdn_name"
fi fi
fi fi
} }

View File

@ -67,7 +67,11 @@ if [ "$reason" = PREINIT ]; then
rm -f "$ypbind_dir/$interface" rm -f "$ypbind_dir/$interface"
elif $if_up || $if_down; then elif $if_up || $if_down; then
if [ -n "$new_nis_domain" ]; then if [ -n "$new_nis_domain" ]; then
make_yp_binding if valid_domainname "$new_nis_domain"; then
make_yp_binding
else
syslog err "Invalid NIS domain name: $new_nis_domain"
fi
elif [ -n "$old_nis_domain" ]; then elif [ -n "$old_nis_domain" ]; then
restore_yp_binding restore_yp_binding
fi fi

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2006-2010 Roy Marples .\" Copyright (c) 2006-2011 Roy Marples
.\" All rights reserved .\" All rights reserved
.\" .\"
.\" Redistribution and use in source and binary forms, with or without .\" Redistribution and use in source and binary forms, with or without
@ -22,7 +22,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE. .\" SUCH DAMAGE.
.\" .\"
.Dd August 24, 2010 .Dd March 23, 2011
.Dt DHCPCD-RUN-HOOKS 8 SMM .Dt DHCPCD-RUN-HOOKS 8 SMM
.Os .Os
.Sh NAME .Sh NAME
@ -135,3 +135,11 @@ in a lexical order and then finally
.An Roy Marples Aq roy@marples.name .An Roy Marples Aq roy@marples.name
.Sh BUGS .Sh BUGS
Please report them to http://roy.marples.name/projects/dhcpcd Please report them to http://roy.marples.name/projects/dhcpcd
.Sh SECURITY CONSIDERATIONS
Little validation of DHCP options is done in dhcpcd itself.
Instead, it is up to the hooks to handle any validation needed.
To this end, some helper functions are provided, such as valid_domainname as
used by the
.Pa 20-resolv.conf
hook to ensure that the hostname is not set to an invalid value.
valid_path is also provided, but is currently unused by a stock hook script.

View File

@ -142,11 +142,51 @@ syslog()
[ -n "$lvl" ] && shift [ -n "$lvl" ] && shift
if [ -n "$*" ]; then if [ -n "$*" ]; then
if type logger >/dev/null 2>&1; then if type logger >/dev/null 2>&1; then
logger -t dhcpcd -p daemon."$lvl" -s "$*" logger -t dhcpcd -p daemon."$lvl" -is "$interface: $*"
fi fi
fi fi
} }
# Check for a valid domain name as per RFC1123 with the exception of
# allowing - and _ as they seem to be widely used.
valid_domainname()
{
local name="$1" label
[ -z "$name" -o ${#name} -gt 255 ] && return 1
while [ -n "$name" ]; do
label="${name%%.*}"
[ -z "$label" -o ${#label} -gt 63 ] && return 1
case "$label" in
-*|_*|*-|*_) return 1;;
*[![:alnum:]-_]*) return 1;;
esac
[ "$name" = "${name#*.}" ] && break
name="${name#*.}"
done
return 0
}
valid_domainname_list()
{
local name
for name in $@; do
valid_domainname "$name" || return $?
done
return 0
}
# Check for a valid path
valid_path()
{
case "$@" in
*[![:alnum:]#%+-_:\.,@~\\/\[\]=\ ]*) return 1;;
esac
return 0
}
# Check a system service exists # Check a system service exists
service_exists() service_exists()
{ {

View File

@ -528,7 +528,17 @@ handle_dhcp(struct interface *iface, struct dhcp_message **dhcpp, const struct i
log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from); log_dhcp(LOG_WARNING, "reject DHCP", iface, dhcp, from);
return; return;
} }
} }
/* Ensure that the address offered is valid */
if ((type == 0 || type == DHCP_OFFER || type == DHCP_ACK) &&
(dhcp->ciaddr == INADDR_ANY || dhcp->ciaddr == INADDR_BROADCAST) &&
(dhcp->yiaddr == INADDR_ANY || dhcp->yiaddr == INADDR_BROADCAST))
{
log_dhcp(LOG_WARNING, "reject invalid address",
iface, dhcp, from);
return;
}
/* No NAK, so reset the backoff */ /* No NAK, so reset the backoff */
state->nakoff = 1; state->nakoff = 1;
@ -631,7 +641,7 @@ handle_dhcp_packet(void *arg)
const uint8_t *pp; const uint8_t *pp;
ssize_t bytes; ssize_t bytes;
struct in_addr from; struct in_addr from;
int i; int i, partialcsum = 0;
/* We loop through until our buffer is empty. /* We loop through until our buffer is empty.
* The benefit is that if we get >1 DHCP packet in our buffer and * The benefit is that if we get >1 DHCP packet in our buffer and
@ -639,10 +649,10 @@ handle_dhcp_packet(void *arg)
packet = xmalloc(udp_dhcp_len); packet = xmalloc(udp_dhcp_len);
for(;;) { for(;;) {
bytes = get_raw_packet(iface, ETHERTYPE_IP, bytes = get_raw_packet(iface, ETHERTYPE_IP,
packet, udp_dhcp_len); packet, udp_dhcp_len, &partialcsum);
if (bytes == 0 || bytes == -1) if (bytes == 0 || bytes == -1)
break; break;
if (valid_udp_packet(packet, bytes, &from) == -1) { if (valid_udp_packet(packet, bytes, &from, partialcsum) == -1) {
syslog(LOG_ERR, "%s: invalid UDP packet from %s", syslog(LOG_ERR, "%s: invalid UDP packet from %s",
iface->name, inet_ntoa(from)); iface->name, inet_ntoa(from));
continue; continue;
@ -919,7 +929,11 @@ start_discover(void *arg)
else else
add_timeout_sec(ifo->timeout, start_ipv4ll, iface); add_timeout_sec(ifo->timeout, start_ipv4ll, iface);
} }
syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name); if (ifo->options & DHCPCD_REQUEST)
syslog(LOG_INFO, "%s: broadcasting for a lease (requesting %s)",
iface->name, inet_ntoa(ifo->req_addr));
else
syslog(LOG_INFO, "%s: broadcasting for a lease", iface->name);
send_discover(iface); send_discover(iface);
} }
@ -2011,17 +2025,20 @@ main(int argc, char **argv)
} }
} }
} }
if (options & DHCPCD_MASTER)
i = if_options->timeout;
else
i = ifaces->state->options->timeout;
if (opt == 0 && if (opt == 0 &&
options & DHCPCD_LINK && options & DHCPCD_LINK &&
!(options & DHCPCD_WAITIP)) !(options & DHCPCD_WAITIP))
{ {
syslog(LOG_WARNING, "no interfaces have a carrier"); syslog(LOG_WARNING, "no interfaces have a carrier");
daemonise(); daemonise();
} else if (if_options->timeout > 0) { } else if (i > 0) {
if (options & DHCPCD_IPV4LL) if (options & DHCPCD_IPV4LL)
options |= DHCPCD_TIMEOUT_IPV4LL; options |= DHCPCD_TIMEOUT_IPV4LL;
add_timeout_sec(if_options->timeout, add_timeout_sec(i, handle_exit_timeout, NULL);
handle_exit_timeout, NULL);
} }
} }
free_options(if_options); free_options(if_options);

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2008 Roy Marples <roy@marples.name> * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2008 Roy Marples <roy@marples.name> * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

View File

@ -625,11 +625,16 @@ parse_option(struct if_options *ifo, int opt, const char *arg)
} }
p++; p++;
if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) { if (strncmp(arg, "ip_address=", strlen("ip_address=")) == 0) {
if (parse_addr(&ifo->req_addr, &ifo->req_mask, p) != 0) if (parse_addr(&ifo->req_addr,
ifo->req_mask.s_addr == 0 ? &ifo->req_mask : NULL,
p) != 0)
return -1; return -1;
ifo->options |= DHCPCD_STATIC; ifo->options |= DHCPCD_STATIC;
ifo->options &= ~DHCPCD_INFORM; ifo->options &= ~DHCPCD_INFORM;
} else if (strncmp(arg, "subnet_mask=", strlen("subnet_mask=")) == 0) {
if (parse_addr(&ifo->req_mask, NULL, p) != 0)
return -1;
} else if (strncmp(arg, "routes=", strlen("routes=")) == 0 || } else if (strncmp(arg, "routes=", strlen("routes=")) == 0 ||
strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 || strncmp(arg, "static_routes=", strlen("static_routes=")) == 0 ||
strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 || strncmp(arg, "classless_static_routes=", strlen("classless_static_routes=")) == 0 ||

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2008 Roy Marples <roy@marples.name> * Copyright (c) 2006-2010 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2010 Roy Marples <roy@marples.name> * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -726,7 +726,8 @@ get_udp_data(const uint8_t **data, const uint8_t *udp)
} }
int int
valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from) valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from,
int noudpcsum)
{ {
struct udp_dhcp_packet packet; struct udp_dhcp_packet packet;
uint16_t bytes, udpsum; uint16_t bytes, udpsum;
@ -754,19 +755,22 @@ valid_udp_packet(const uint8_t *data, size_t data_len, struct in_addr *from)
errno = EINVAL; errno = EINVAL;
return -1; return -1;
} }
udpsum = packet.udp.uh_sum;
packet.udp.uh_sum = 0; if (noudpcsum == 0) {
packet.ip.ip_hl = 0; udpsum = packet.udp.uh_sum;
packet.ip.ip_v = 0; packet.udp.uh_sum = 0;
packet.ip.ip_tos = 0; packet.ip.ip_hl = 0;
packet.ip.ip_len = packet.udp.uh_ulen; packet.ip.ip_v = 0;
packet.ip.ip_id = 0; packet.ip.ip_tos = 0;
packet.ip.ip_off = 0; packet.ip.ip_len = packet.udp.uh_ulen;
packet.ip.ip_ttl = 0; packet.ip.ip_id = 0;
packet.ip.ip_sum = 0; packet.ip.ip_off = 0;
if (udpsum && checksum(&packet, bytes) != udpsum) { packet.ip.ip_ttl = 0;
errno = EINVAL; packet.ip.ip_sum = 0;
return -1; if (udpsum && checksum(&packet, bytes) != udpsum) {
errno = EINVAL;
return -1;
}
} }
return 0; return 0;

View File

@ -1,6 +1,6 @@
/* /*
* dhcpcd - DHCP client daemon * dhcpcd - DHCP client daemon
* Copyright (c) 2006-2010 Roy Marples <roy@marples.name> * Copyright (c) 2006-2011 Roy Marples <roy@marples.name>
* All rights reserved * All rights reserved
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@ -84,6 +84,7 @@ struct rt {
struct in_addr net; struct in_addr net;
struct in_addr gate; struct in_addr gate;
const struct interface *iface; const struct interface *iface;
struct in_addr src;
struct rt *next; struct rt *next;
}; };
@ -139,14 +140,14 @@ extern const size_t udp_dhcp_len;
ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t, ssize_t make_udp_packet(uint8_t **, const uint8_t *, size_t,
struct in_addr, struct in_addr); struct in_addr, struct in_addr);
ssize_t get_udp_data(const uint8_t **, const uint8_t *); ssize_t get_udp_data(const uint8_t **, const uint8_t *);
int valid_udp_packet(const uint8_t *, size_t, struct in_addr *); int valid_udp_packet(const uint8_t *, size_t, struct in_addr *, int);
int open_socket(struct interface *, int); int open_socket(struct interface *, int);
ssize_t send_packet(const struct interface *, struct in_addr, ssize_t send_packet(const struct interface *, struct in_addr,
const uint8_t *, ssize_t); const uint8_t *, ssize_t);
ssize_t send_raw_packet(const struct interface *, int, ssize_t send_raw_packet(const struct interface *, int,
const void *, ssize_t); const void *, ssize_t);
ssize_t get_raw_packet(struct interface *, int, void *, ssize_t); ssize_t get_raw_packet(struct interface *, int, void *, ssize_t, int *);
int init_sockets(void); int init_sockets(void);
int open_link_socket(void); int open_link_socket(void);