bpf-filter.h | 2 + client.c | 1 + common.c | 4 +- common.h | 2 +- dhcp.c | 5 +++ dhcpcd-hooks/10-mtu | 2 +- dhcpcd-hooks/20-resolv.conf | 33 ++++++++++++++++++++---- dhcpcd-hooks/29-lookup-hostname | 2 +- dhcpcd-hooks/30-hostname | 13 ++++++++- dhcpcd-hooks/50-dhcpcd-compat | 6 ++-- dhcpcd-hooks/50-ntp.conf | 50 ++++++++++++++++++++++++------------ dhcpcd-hooks/50-yp.conf | 5 +++ dhcpcd-run-hooks.8.in | 8 ++++++ dhcpcd-run-hooks.in | 52 +++++++++++++++++++++++++++++++++++++++ dhcpcd.c | 11 +++----- dhcpcd.conf | 9 ++++++ if-linux.c | 38 ++++++++++++++++++++++------ logger.c | 13 ++++++++- net.c | 18 ++++++------- 19 files changed, 214 insertions(+), 60 deletions(-) diff --git a/bpf-filter.h b/bpf-filter.h index 881f678..4e48951 100644 --- a/bpf-filter.h +++ b/bpf-filter.h @@ -24,6 +24,8 @@ * SUCH DAMAGE. */ +#include + #ifndef BPF_ETHCOOK # define BPF_ETHCOOK 0 #endif diff --git a/client.c b/client.c index 90657b3..363741d 100644 --- a/client.c +++ b/client.c @@ -29,6 +29,7 @@ #include #include #include +#include #ifdef __linux__ # include diff --git a/common.c b/common.c index da22a5c..8d88884 100644 --- a/common.c +++ b/common.c @@ -93,10 +93,10 @@ uint32_t arc4random(void) seed = time(0); if (fd >= 0) close(fd); - srandom(seed); + srand(seed); } - return (uint32_t)random(); + return (uint32_t)rand(); } #endif diff --git a/common.h b/common.h index 2522663..6959be2 100644 --- a/common.h +++ b/common.h @@ -44,7 +44,7 @@ #endif #ifndef HAVE_ARC4RANDOM -# ifdef __GLIBC__ +# if defined(__GLIBC__) || defined(__dietlibc__) uint32_t arc4random(void); #else # define HAVE_ARC4RANDOM diff --git a/dhcp.c b/dhcp.c index aed06bf..ab7b6ec 100644 --- a/dhcp.c +++ b/dhcp.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "config.h" #include "common.h" @@ -936,6 +937,8 @@ make_message(struct dhcp_message **message, n_params = p; *p++ = 0; for (opt = dhcp_opts; opt->option; opt++) { + if (has_option_mask (options->nomask, opt->option)) + continue; if (!(opt->type & REQUEST || has_option_mask(options->requestmask, opt->option))) continue; @@ -1056,6 +1059,8 @@ print_string(char *s, ssize_t len, int dl, const uint8_t *data) case '$': /* FALLTHROUGH */ case '`': /* FALLTHROUGH */ case '\\': /* FALLTHROUGH */ + case '|': /* FALLTHROUGH */ + case '&': if (s) { if (len < 3) { errno = ENOBUFS; diff --git a/dhcpcd-hooks/10-mtu b/dhcpcd-hooks/10-mtu index 4265b48..08c9167 100644 --- a/dhcpcd-hooks/10-mtu +++ b/dhcpcd-hooks/10-mtu @@ -1,5 +1,5 @@ # Configure the MTU for the interface if [ -n "${new_interface_mtu}" ]; then - ifconfig "${interface}" mtu "${new_interface_mtu}" + ip link set ${interface} mtu ${new_interface_mtu} fi diff --git a/dhcpcd-hooks/20-resolv.conf b/dhcpcd-hooks/20-resolv.conf index e757ddf..717c05d 100644 --- a/dhcpcd-hooks/20-resolv.conf +++ b/dhcpcd-hooks/20-resolv.conf @@ -7,11 +7,12 @@ # or dnsmasq. This is important as the libc resolver isn't that powerful. resolv_conf_dir="${state_dir}/resolv.conf" +uchrooted=/sbin/update_chrooted build_resolv_conf() { local cf="/etc/resolv.conf.${interface}" - local interfaces= header= search= srvs= servers= x= + local interfaces= header= domain= search= srvs= servers= x= # Build a list of interfaces interfaces=$(list_interfaces "${resolv_conf_dir}") @@ -24,8 +25,11 @@ build_resolv_conf() done # Build the search list - search=$(cd "${resolv_conf_dir}"; \ + domain=$(cd "$resolv_conf_dir"; \ + key_get_value "domain " ${interfaces}) + search=$(cd "$resolv_conf_dir"; \ key_get_value "search " ${interfaces}) + [ -n "$domain" ] && domain="domain $domain\n" [ -n "${search}" ] && search="search $(uniqify ${search})\n" # Build the nameserver list @@ -45,13 +49,14 @@ build_resolv_conf() else echo "# /etc/resolv.conf.head can replace this line" >> "${cf}" fi - printf "${search}${servers}" >> "${cf}" + printf "${domain}${search}${servers}" >> "${cf}" if [ -f /etc/resolv.conf.tail ]; then cat /etc/resolv.conf.tail >> "${cf}" else echo "# /etc/resolv.conf.tail can replace this line" >> "${cf}" fi mv -f "${cf}" /etc/resolv.conf + [ ! -x "$uchrooted" ] || "$uchrooted" conf } add_resolv_conf() @@ -66,10 +71,26 @@ add_resolv_conf() return $? fi + if [ -n "$new_domain_name" ]; then + set -- $new_domain_name + new_domain_name="$1" + 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 + if [ -z "$new_domain_search" -a -n "$2" ]; then + new_domain_search="$@" + fi + fi + if [ -n "${new_domain_search}" ]; then - conf="${conf}search ${new_domain_search}\n" - elif [ -n "${new_domain_name}" ]; then - conf="${conf}search ${new_domain_name}\n" + if valid_domainname_list "$new_domain_search"; then + conf="${conf}search ${new_domain_search}\n" + else + syslog err "Invalid domain name: $new_domain_search" + fi fi for x in ${new_domain_name_servers}; do conf="${conf}nameserver ${x}\n" diff --git a/dhcpcd-hooks/29-lookup-hostname b/dhcpcd-hooks/29-lookup-hostname index 3dfade3..f592c31 100644 --- a/dhcpcd-hooks/29-lookup-hostname +++ b/dhcpcd-hooks/29-lookup-hostname @@ -8,7 +8,7 @@ lookup_hostname() if type dig >/dev/null 2>&1; then h=`dig +short -x ${new_ip_address}` if [ $? = 0 ]; then - echo "${h}" | sed 's/\.$//' + echo "${h%.}" return 0 fi elif type host >/dev/null 2>&1; then diff --git a/dhcpcd-hooks/30-hostname b/dhcpcd-hooks/30-hostname index b2e5fc8..903c573 100644 --- a/dhcpcd-hooks/30-hostname +++ b/dhcpcd-hooks/30-hostname @@ -12,13 +12,22 @@ need_hostname() esac } +try_hostname() +{ + if valid_domainname "$1"; then + hostname "$1" + else + syslog err "Invalid hostname: $1" + fi +} + set_hostname() { if need_hostname; then if [ -n "${new_host_name}" ]; then - hostname "${new_host_name}" + try_hostname "${new_host_name}" else - hostname "${new_fqdn_name}" + try_hostname "${new_fqdn_name}" fi fi } diff --git a/dhcpcd-hooks/50-dhcpcd-compat b/dhcpcd-hooks/50-dhcpcd-compat index bb31fd3..f37e7b0 100644 --- a/dhcpcd-hooks/50-dhcpcd-compat +++ b/dhcpcd-hooks/50-dhcpcd-compat @@ -30,12 +30,12 @@ BOUND|INFORM|REBIND|REBOOT|TEST|TIMEOUT|IPV4LL) x="new";; esac if [ "${reason}" != "down" ]; then - rm -f /var/lib/dhcpcd-"${INTERFACE}".info + rm -f /var/lib/dhcpcd/dhcpcd-"${INTERFACE}".info for x in IPADDR INTERFACE NETMASK BROADCAST NETWORK DHCPSID GATEWAYS \ DNSSERVERS DNSDOMAIN DNSSEARCH NISDOMAIN NISSERVERS \ NTPSERVERS GATEWAY DNS; do - eval echo "${x}=\'\$${x}\'" >> /var/lib/dhcpcd-"${INTERFACE}".info + eval echo "${x}=\'\$${x}\'" >> /var/lib/dhcpcd/dhcpcd-"${INTERFACE}".info done fi -set -- /var/lib/dhcpcd-"${INTERFACE}".info "${x}" +set -- /var/lib/dhcpcd/dhcpcd-"${INTERFACE}".info "${x}" diff --git a/dhcpcd-hooks/50-ntp.conf b/dhcpcd-hooks/50-ntp.conf index 8c92f27..9c34a21 100644 --- a/dhcpcd-hooks/50-ntp.conf +++ b/dhcpcd-hooks/50-ntp.conf @@ -8,24 +8,21 @@ # NTP_CONF=/usr/pkg/etc/ntpd.conf # to use openntpd from pkgsrc instead of the system provided ntp. -# Detect OpenRC or BSD rc -# Distributions may want to just have their command here instead of this -if type rc-service >/dev/null 2>&1 && rc-service --exists ntpd; then - ntpd_restart_cmd="rc-service ntpd -- --ifstarted --quiet restart" -elif [ -x /etc/rc.d/ntpd ]; then - ntpd_restart_cmd="/etc/rc.d/ntpd status && /etc/rc.d/ntpd restart" -elif [ -x /usr/local/etc/rc.d/ntpd ]; then - ntpd_restart_cmd="/usr/local/etc/rc.d/ntpd status && /usr/local/etc/rc.d/ntpd restart" +if [ -x /etc/init.d/ntpd ]; then + ntpd_restart_cmd="/sbin/service ntpd condrestart" fi ntp_conf_dir="${state_dir}/ntp.conf" -ntp_conf=${NTP_CONF:-/etc/ntp.conf} build_ntp_conf() { + local ntp_conf="$1" + local server_keyword=${2:-server} local cf="${ntp_conf}.${interface}" local interfaces= header= srvs= servers= x= + [ -w ${ntp_conf} ] || return 1 + # Build a list of interfaces interfaces=$(list_interfaces "${ntp_conf_dir}") @@ -40,7 +37,7 @@ build_ntp_conf() key_get_value "server " ${interfaces}) if [ -n "${srvs}" ]; then for x in $(uniqify ${srvs}); do - servers="${servers}server ${x}\n" + servers="${servers}${server_keyword} ${x}\n" done fi fi @@ -48,17 +45,36 @@ build_ntp_conf() # Merge our config into ntp.conf [ -e "${cf}" ] && rm -f "${cf}" remove_markers "${signature_base}" "${signature_base_end}" \ - /etc/ntp.conf > "${cf}" + ${ntp_conf} > "${cf}" if [ -n "${servers}" ]; then echo "${signature_base}${header:+ ${from} }${header}" >> "${cf}" - printf "${search}${servers}" >> "${cf}" + printf "${servers}" >> "${cf}" echo "${signature_base_end}${header:+ ${from} }${header}" >> "${cf}" fi - # If we changed anything, restart ntpd - if change_file "${ntp_conf}" "${cf}"; then - [ -n "${ntpd_restart_cmd}" ] && eval ${ntpd_restart_cmd} + # If we changed anything, will restart ntpd + change_file "${ntp_conf}" "${cf}" +} + + +build_all_ntp_conf() +{ + local ret=1 + if [ -n "$NTP_CONF" ]; then + build_ntp_conf "$NTP_CONF" && ret=0 + else + #for openntpd will be used "servers" keyword instead "server" + build_ntp_conf "/etc/ntpd.conf" "servers" && ret=0 + + build_ntp_conf "/etc/ntp.conf" && ret=0 fi + + return ${ret} +} + +restart_ntpd() +{ + [ -n "${ntpd_restart_cmd}" ] && eval ${ntpd_restart_cmd} } add_ntp_conf() @@ -72,7 +88,7 @@ add_ntp_conf() echo "server ${x}" >> "${cf}" done fi - build_ntp_conf + build_all_ntp_conf && restart_ntpd } remove_ntp_conf() @@ -80,7 +96,7 @@ remove_ntp_conf() if [ -e "${ntp_conf_dir}/${interface}" ]; then rm "${ntp_conf_dir}/${interface}" fi - build_ntp_conf + build_all_ntp_conf && restart_ntpd } case "${reason}" in diff --git a/dhcpcd-hooks/50-yp.conf b/dhcpcd-hooks/50-yp.conf index a2296eb..99506aa 100644 --- a/dhcpcd-hooks/50-yp.conf +++ b/dhcpcd-hooks/50-yp.conf @@ -13,6 +13,11 @@ make_yp_conf() rm -f "${cf}" echo "${signature}" > "${cf}" if [ -n "${new_nis_domain}" ]; then + if ! valid_domainname "$new_nis_domain"; then + syslog err "Invalid NIS domain name: $new_nis_domain" + rm -f "$cf" + return 1 + fi domainname "${new_nis_domain}" if [ -n "${new_nis_servers}" ]; then prefix="domain ${new_nis_domain} server " diff --git a/dhcpcd-run-hooks.8.in b/dhcpcd-run-hooks.8.in index 6776cf8..ae8846d 100644 --- a/dhcpcd-run-hooks.8.in +++ b/dhcpcd-run-hooks.8.in @@ -112,3 +112,11 @@ in a lexical order and then finally .An Roy Marples .Sh BUGS 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. diff --git a/dhcpcd-run-hooks.in b/dhcpcd-run-hooks.in index a848260..c53cf8e 100644 --- a/dhcpcd-run-hooks.in +++ b/dhcpcd-run-hooks.in @@ -115,6 +115,58 @@ restore_conf() mv -f "$1"-pre."${interface}" "$1" } +# Write a syslog entry +syslog() +{ + local lvl="$1" + + [ -n "$lvl" ] && shift + if [ -n "$*" ]; then + if type logger >/dev/null 2>&1; then + logger -t dhcpcd -p daemon."$lvl" -s "$*" + 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 +} # We source each script into this one so that scripts run earlier can # remove variables from the environment so later scripts don't see them. diff --git a/dhcpcd.c b/dhcpcd.c index 1b2e600..6543b56 100644 --- a/dhcpcd.c +++ b/dhcpcd.c @@ -602,7 +602,6 @@ main(int argc, char **argv) struct options *options; int opt; int option_index = 0; - char *prefix; pid_t pid; int debug = 0; int i, r; @@ -620,7 +619,7 @@ main(int argc, char **argv) /* Saves calling fflush(stream) in the logger */ setlinebuf(stdout); openlog(PACKAGE, LOG_PID, LOG_DAEMON); - setlogprefix(PACKAGE ": "); + setlogprefix(PACKAGE); options = xzalloc(sizeof(*options)); options->options |= DHCPCD_GATEWAY | DHCPCD_DAEMONISE; @@ -653,7 +652,8 @@ main(int argc, char **argv) /* Ensure that the hostname is NULL terminated */ options->hostname[HOSTNAME_MAX_LEN] = '\0'; if (strcmp(options->hostname, "(none)") == 0 || - strcmp(options->hostname, "localhost") == 0) + strcmp(options->hostname, "localhost") == 0 || + strcmp(options->hostname, "localhost.localdomain") == 0) options->hostname[0] = '\0'; while ((opt = getopt_long(argc, argv, OPTS EXTRA_OPTS, @@ -849,12 +849,9 @@ main(int argc, char **argv) } } - prefix = xmalloc(sizeof(char) * (IF_NAMESIZE + 3)); - snprintf(prefix, IF_NAMESIZE, "%s: ", options->interface); - setlogprefix(prefix); + setlogprefix(options->interface); snprintf(options->pidfile, sizeof(options->pidfile), PIDFILE, options->interface); - free(prefix); if (options->request_address.s_addr == 0 && (options->options & DHCPCD_INFORM || diff --git a/dhcpcd.conf b/dhcpcd.conf index cce1795..8be414d 100644 --- a/dhcpcd.conf +++ b/dhcpcd.conf @@ -11,3 +11,12 @@ option ntp_servers # However, a lot of buggy DHCP servers set invalid MTUs so this is not # enabled by default. #option interface_mtu + +# Don't attempt to obtain an IPv4LL address if we failed to get one via DHCP. +noipv4ll + +#Don`t attempt to lookup the hostname in DNS by default +nohook lookup-hostname + +#Don't send any ARP requests. +noarp diff --git a/if-linux.c b/if-linux.c index a754554..19affcb 100644 --- a/if-linux.c +++ b/if-linux.c @@ -56,8 +56,6 @@ #include "dhcp.h" #include "net.h" -#define BUFFERLEN 256 - int open_link_socket(struct interface *iface) { @@ -83,14 +81,38 @@ get_netlink(int fd, int flags, int (*callback)(struct nlmsghdr *, const char *), const char *ifname) { - char *buffer = NULL; - ssize_t bytes; + char *buf = NULL, *nbuf; + ssize_t buflen = 0, bytes; struct nlmsghdr *nlm; int r = -1; - buffer = xzalloc(sizeof(char) * BUFFERLEN); for (;;) { - bytes = recv(fd, buffer, BUFFERLEN, flags); + bytes = recv(fd, NULL, 0, + flags | MSG_PEEK | MSG_DONTWAIT | MSG_TRUNC); + if (bytes == -1) { + if (errno == EAGAIN) { + r = 0; + goto eexit; + } + if (errno == EINTR) + continue; + goto eexit; + } else if (bytes == buflen) { + /* Support kernels older than 2.6.22 */ + if (bytes == 0) + bytes = 512; + else + bytes *= 2; + } + if (buflen < bytes) { + /* Alloc 1 more so we work with older kernels */ + buflen = bytes + 1; + nbuf = realloc(buf, buflen); + if (nbuf == NULL) + goto eexit; + buf = nbuf; + } + bytes = recv(fd, buf, buflen, flags); if (bytes == -1) { if (errno == EAGAIN) { r = 0; @@ -100,7 +122,7 @@ get_netlink(int fd, int flags, continue; goto eexit; } - for (nlm = (struct nlmsghdr *)buffer; + for (nlm = (struct nlmsghdr *)buf; NLMSG_OK(nlm, (size_t)bytes); nlm = NLMSG_NEXT(nlm, bytes)) { @@ -111,7 +133,7 @@ get_netlink(int fd, int flags, } eexit: - free(buffer); + free(buf); return r; } diff --git a/logger.c b/logger.c index 15c6cf7..849c108 100644 --- a/logger.c +++ b/logger.c @@ -36,7 +36,7 @@ #include "logger.h" static int loglevel = LOG_INFO; -static char logprefix[12] = {0}; +static char *logprefix = NULL; void setloglevel(int level) @@ -47,7 +47,16 @@ setloglevel(int level) void setlogprefix(const char *prefix) { - strlcpy(logprefix, prefix, sizeof(logprefix)); + size_t size = strlen(prefix) + 3; + + free (logprefix); + logprefix = malloc(size); + if(logprefix) + snprintf(logprefix, size, "%s: ", prefix); + else { + syslog(LOG_ERR, "setlogprefix: memory exhausted"); + exit(EXIT_FAILURE); + } } void diff --git a/net.c b/net.c index 29344f8..e5b2e80 100644 --- a/net.c +++ b/net.c @@ -40,9 +40,7 @@ #endif #include #include -#define __FAVOR_BSD /* Nasty glibc hack so we can use BSD semantics for UDP */ #include -#undef __FAVOR_BSD #ifdef SIOCGIFMEDIA #include #endif @@ -602,11 +600,11 @@ make_udp_packet(uint8_t **packet, const uint8_t *data, size_t length, else ip->ip_dst.s_addr = dest.s_addr; - udp->uh_sport = htons(DHCP_CLIENT_PORT); - udp->uh_dport = htons(DHCP_SERVER_PORT); - udp->uh_ulen = htons(sizeof(*udp) + length); - ip->ip_len = udp->uh_ulen; - udp->uh_sum = checksum(udpp, sizeof(*udpp)); + udp->source = htons(DHCP_CLIENT_PORT); + udp->dest = htons(DHCP_SERVER_PORT); + udp->len = htons(sizeof(*udp) + length); + ip->ip_len = udp->len; + udp->check = checksum(udpp, sizeof(*udpp)); ip->ip_v = IPVERSION; ip->ip_hl = 5; @@ -654,12 +652,12 @@ valid_udp_packet(const uint8_t *data, size_t data_len) errno = EINVAL; return -1; } - udpsum = packet.udp.uh_sum; - packet.udp.uh_sum = 0; + udpsum = packet.udp.check; + packet.udp.check = 0; packet.ip.ip_hl = 0; packet.ip.ip_v = 0; packet.ip.ip_tos = 0; - packet.ip.ip_len = packet.udp.uh_ulen; + packet.ip.ip_len = packet.udp.len; packet.ip.ip_id = 0; packet.ip.ip_off = 0; packet.ip.ip_ttl = 0;