Hello community, here is the log from the commit of package dhcpcd for openSUSE:Factory checked in at Mon Sep 19 21:08:35 CEST 2011.
-------- --- dhcpcd/dhcpcd.changes 2010-11-30 15:12:50.000000000 +0100 +++ /mounts/work_src_done/STABLE/dhcpcd/dhcpcd.changes 2011-04-19 10:09:14.000000000 +0200 @@ -1,0 +2,20 @@ +Tue Apr 19 07:43:22 UTC 2011 - [email protected] + +- Fixed a bug causing a crash in the sip option check, when the + option data can't be decoded (bnc#687850). +- Relaxed the check of the domain-name option causing a regression, + when the server is misusing it to provide a domain list and does + not provide it using the domain-search option (bnc#675052). + +------------------------------------------------------------------- +Thu Mar 31 13:47:05 UTC 2011 - [email protected] + +- bnc#675052 - discard string options such as host and domain names + containing disallowed characters or beeing too long. This proctive + patch limits root-path to a-zA-Z0-9, #%+-_:.,@~/\[]= and a space + (CVE-2011-0997). +- fixed to read netbios scope from info file as string +- bnc#668194 - handle packets with xen partial UDP checksums +- bnc#657402 - send renew messages as unicast directly to the server + +------------------------------------------------------------------- calling whatdependson for head-i586 New: ---- dhcpcd-3.2.3-option-checks.diff dhcpcd-3.2.3-renew-unicast.diff dhcpcd-3.2.3-xen-checksum.diff ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ dhcpcd.spec ++++++ --- /var/tmp/diff_new_pack.I88TLE/_old 2011-09-19 21:08:30.000000000 +0200 +++ /var/tmp/diff_new_pack.I88TLE/_new 2011-09-19 21:08:30.000000000 +0200 @@ -1,7 +1,7 @@ # -# spec file for package dhcpcd (Version 3.2.3) +# spec file for package dhcpcd # -# Copyright (c) 2010 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2011 SUSE LINUX Products GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ Name: dhcpcd Version: 3.2.3 -Release: 63 +Release: 47.<RELEASE6> Summary: A DHCP Client Daemon Url: http://developer.berlios.de/projects/dhcpcd/ #Source: http://prdownload.berlios.de/dhcpcd/dhcpcd-3.2.3.tar.bz2 @@ -46,6 +46,9 @@ Patch17: bnc#564441.diff Patch18: bnc#577402.diff Patch19: bnc#579438.diff +Patch20: dhcpcd-3.2.3-renew-unicast.diff +Patch21: dhcpcd-3.2.3-xen-checksum.diff +Patch22: dhcpcd-3.2.3-option-checks.diff License: GPLv2+ Group: Productivity/Networking/Boot/Clients Provides: dhcp_client @@ -87,6 +90,9 @@ %patch17 -p0 %patch18 -p0 %patch19 -p0 +%patch20 -p0 +%patch21 -p0 +%patch22 -p0 cp %{S:1} . cp %{S:3} . ++++++ dhcpcd-3.2.3-more-params.diff ++++++ --- /var/tmp/diff_new_pack.I88TLE/_old 2011-09-19 21:08:30.000000000 +0200 +++ /var/tmp/diff_new_pack.I88TLE/_new 2011-09-19 21:08:30.000000000 +0200 @@ -186,7 +186,7 @@ + else if (strcmp (var, "LOGSERVER") == 0) + dhcp->logservers = parse_addresses (value, "LOGSERVER"); + else if (strcmp (var, "NETBIOSSCOPE") == 0) -+ parse_uint (&dhcp->netbiosscope, value, "NETBIOSSCOPE"); ++ dhcp->netbiosscope = xstrdup (value); + else if (strcmp (var, "NETBIOSNAMESERVER") == 0) + dhcp->netbiosnameservers = parse_addresses (value, "NETBIOSNAMESERVER"); + else if (strcmp (var, "NETBIOSDDSERVER") == 0) ++++++ dhcpcd-3.2.3-option-checks.diff ++++++ --- configure.c +++ configure.c @@ -540,7 +540,7 @@ static char *lookuphostname (char *hostname, const dhcp_t *dhcp, char *addr; struct addrinfo hints; struct addrinfo *res = NULL; - int result; + int result, check; logger (LOG_DEBUG, "Looking up hostname via DNS"); addr = xmalloc (sizeof (char) * NI_MAXHOST); @@ -565,9 +565,10 @@ static char *lookuphostname (char *hostname, const dhcp_t *dhcp, result = getaddrinfo (addr, "0", &hints, &res); if (res) freeaddrinfo (res); - if (result == 0) + check = check_domain_name(addr, strlen(addr), 0); + if (result == 0 || check != 0) logger (LOG_ERR, "malicious PTR record detected"); - if (result == 0 || ! *addr) { + if (result == 0 || ! *addr || check != 0) { free (addr); return (NULL); } @@ -842,12 +843,12 @@ int configure (const options_t *options, interface_t *iface, exec_script (options->script, iface->infofile, "up"); curhostname = xmalloc (sizeof (char) * MAXHOSTNAMELEN); - *curhostname = '\0'; + memset(curhostname, 0, MAXHOSTNAMELEN); - gethostname (curhostname, MAXHOSTNAMELEN); + gethostname (curhostname, MAXHOSTNAMELEN - 1); + curhostname[MAXHOSTNAMELEN - 1] = '\0'; if (options->dohostname || - strlen (curhostname) == 0 || - strcmp (curhostname, "(none)") == 0 || + check_domain_name(curhostname, strlen (curhostname), 0) != 0 || strcmp (curhostname, "localhost") == 0) { newhostname = xmalloc (sizeof (char) * MAXHOSTNAMELEN); --- dhcp.c +++ dhcp.c @@ -41,6 +41,8 @@ #include <stdint.h> #include <stdlib.h> #include <string.h> +#include <stddef.h> +#include <ctype.h> #include "config.h" @@ -316,6 +318,7 @@ ssize_t send_message (const interface_t *iface, const dhcp_t *dhcp, *p++ = DHCP_LOGSERVER; *p++ = DHCP_NETBIOSNAMESERVER; *p++ = DHCP_NETBIOSDDSERVER; + *p++ = DHCP_NETBIOSNODETYPE; *p++ = DHCP_NETBIOSSCOPE; } @@ -646,6 +649,106 @@ static struct route_head *decode_routers (const unsigned char *data, int length) return (head); } +int check_domain_name(const char *ptr, size_t len, int dots) +{ + const char *p; + + /* not empty or complete length not over 255 characters */ + if (len == 0 || len >= 256) + return -1; + + /* consists of [[:alnum:]-]+ labels separated by [.] */ + /* a [_] is against RFC but seems to be "widely used"... */ + for (p=ptr; *p && len-- > 0; p++) { + if ( *p == '-' || *p == '_') { + /* not allowed at begin or end of a label */ + if ((p - ptr) == 0 || len == 0 || p[1] == '.') + return -1; + } else + if ( *p == '.') { + /* each label has to be 1-63 characters; + we allow [.] at the end ('foo.bar.') */ + ptrdiff_t d = p - ptr; + if( d <= 0 || d >= 64) + return -1; + ptr = p + 1; /* jump to the next label */ + if(dots > 0 && len > 0) + dots--; + } else + if ( !isalnum((unsigned char)*p)) { + /* also numbers at the begin are fine */ + return -1; + } + } + return dots ? -1 : 0; +} + +int check_domain_name_list(const char *ptr, size_t len, int dots) +{ + const char *p; + int ret = -1; /* at least one needed */ + + if (!ptr || !len) + return -1; + + for (p=ptr; *p && len > 0; p++, len--) { + if (*p != ' ') + continue; + if (p > ptr) { + if (check_domain_name(ptr, p - ptr, dots) != 0) + return -1; + ret = 0; + } + ptr = p + 1; + } + if (p > ptr) + return check_domain_name(ptr, p - ptr, dots); + else + return ret; +} + +int check_dhcp_option(unsigned char option, const char *ptr, size_t len) +{ + if( !ptr) + return -1; + + switch (option) { + case DHCP_NETBIOSNODETYPE: + if(len == 1 && ((int)*ptr == 1 || (int)*ptr == 2 || + (int)*ptr == 4 || (int)*ptr == 8)) + return 0; + break; + case DHCP_HOSTNAME: + case DHCP_NISDOMAIN: + case DHCP_NETBIOSSCOPE: + return check_domain_name(ptr, len, 0); + break; + case DHCP_SIPSERVER: + case DHCP_DNSDOMAIN: /* accept a list for compatibiliy */ + case DHCP_DNSSEARCH: + return check_domain_name_list(ptr, len, 0); + break; + case DHCP_ROOTPATH: + if( len == 0) + return -1; + for (; *ptr && len-- > 0; ptr++) { + if( !(isalnum((unsigned char)*ptr) || + *ptr == '#' || *ptr == '%' || + *ptr == '+' || *ptr == '-' || + *ptr == '_' || *ptr == ':' || + *ptr == '.' || *ptr == ',' || + *ptr == '@' || *ptr == '~' || + *ptr == '\\' || *ptr == '/' || + *ptr == '[' || *ptr == ']' || + *ptr == '=' || *ptr == ' ')) + return -1; + } + return 0; + break; + } + return 0; +} + int parse_dhcpmessage (dhcp_t *dhcp, const dhcpmessage_t *message) { const unsigned char *p = message->options; @@ -676,8 +779,16 @@ int parse_dhcpmessage (dhcp_t *dhcp, const dhcpmessage_t *message) dhcp->leasedfrom = tv.tv_sec; dhcp->frominfo = false; dhcp->address.s_addr = message->yiaddr; - strlcpy (dhcp->servername, (char *) message->servername, - sizeof (dhcp->servername)); + if (message->servername[0] != '\0' && + check_domain_name((const char *)message->servername, + strlen((const char *)message->servername), 0) != 0) + { + logger (LOG_ERR, "suspect value in SERVERNAME - discarded"); + dhcp->servername[0] = '\0'; + } else { + strlcpy (dhcp->servername, (char *) message->servername, + sizeof (dhcp->servername)); + } #define LEN_ERR \ { \ @@ -798,10 +909,20 @@ parse_start: memcpy (_var, p, (size_t) length); \ memset (_var + length, 0, 1); \ } +#define CHECKOPT(_opt,_var) \ + if(check_dhcp_option(_opt, (const char *)p, length) != 0) { \ + logger (LOG_ERR, "suspect value in option %s - discarded", #_opt); \ + if (_var) free (_var); \ + _var = NULL; \ + } case DHCP_HOSTNAME: + CHECKOPT (DHCP_HOSTNAME, dhcp->hostname) + else GETSTR (dhcp->hostname); break; case DHCP_DNSDOMAIN: + CHECKOPT (DHCP_DNSDOMAIN, dhcp->dnsdomain) + else GETSTR (dhcp->dnsdomain); break; case DHCP_MESSAGE: @@ -809,18 +930,30 @@ parse_start: break; #ifdef ENABLE_INFO case DHCP_ROOTPATH: + CHECKOPT (DHCP_ROOTPATH, dhcp->rootpath) + else GETSTR (dhcp->rootpath); break; #endif #ifdef ENABLE_NIS case DHCP_NISDOMAIN: + CHECKOPT (DHCP_NISDOMAIN, dhcp->nisdomain) + else GETSTR (dhcp->nisdomain); break; #endif case DHCP_NETBIOSNODETYPE: - GETSTR (dhcp->netbiosnodetype); + CHECKOPT (DHCP_NETBIOSNODETYPE, dhcp->netbiosnodetype) + else { + if(dhcp->netbiosnodetype) + free(dhcp->netbiosnodetype); + dhcp->netbiosnodetype = xmalloc ((size_t)length + 1); + snprintf(dhcp->netbiosnodetype, length + 1, "%d", (int)*p); + } break; case DHCP_NETBIOSSCOPE: + CHECKOPT (DHCP_NETBIOSSCOPE, dhcp->netbiosscope) + else GETSTR (dhcp->netbiosscope); break; @@ -863,11 +996,21 @@ parse_start: case DHCP_DNSSEARCH: MIN_LENGTH (1); free (dhcp->dnssearch); + dhcp->dnssearch = NULL; len = decode_search (p, length, NULL); if (len > 0) { - dhcp->dnssearch = xmalloc (len); - decode_search (p, length, - dhcp->dnssearch); + char *str = xmalloc (len); + decode_search (p, length, str); + if (check_dhcp_option(DHCP_DNSSEARCH, + str, len - 1) != 0) { + logger (LOG_ERR, + "suspect value in " + "option %s - discarded", + "DHCP_DNSSEARCH"); + free(str); + } else { + dhcp->dnssearch = str; + } } break; @@ -885,8 +1028,22 @@ parse_start: #ifdef ENABLE_INFO case DHCP_SIPSERVER: - free (dhcp->sipservers); - dhcp->sipservers = decode_sipservers (p,length); + if(dhcp->sipservers) + free (dhcp->sipservers); + dhcp->sipservers = NULL; + { + char *str = decode_sipservers (p,length); + if(str && check_dhcp_option(DHCP_SIPSERVER, + str, strlen(str)) != 0) { + logger (LOG_ERR, + "suspect value in " + "option %s - discarded", + "DHCP_SIPSERVER"); + free(str); + } else { + dhcp->sipservers = str; + } + } break; #endif @@ -922,6 +1079,7 @@ parse_start: #undef LENGTH #undef MIN_LENGTH #undef MULT_LENGTH +#undef CHECKOPT default: logger (LOG_DEBUG, --- dhcp.h +++ dhcp.h @@ -252,4 +252,8 @@ ssize_t send_message (const interface_t *iface, const dhcp_t *dhcp, void free_dhcp (dhcp_t *dhcp); int parse_dhcpmessage (dhcp_t *dhcp, const dhcpmessage_t *message); +int check_dhcp_option(unsigned char option, const char *ptr, size_t len); +int check_domain_name(const char *ptr, size_t len, int dots); +int check_domain_name_list(const char *ptr, size_t len, int dots); + #endif --- dhcpcd.c +++ dhcpcd.c @@ -182,8 +182,10 @@ int main(int argc, char **argv) options->netconfig = false; options->timeout = DEFAULT_TIMEOUT; - gethostname (options->hostname, sizeof (options->hostname)); - if (strcmp (options->hostname, "(none)") == 0 || + memset (options->hostname, 0, sizeof (options->hostname)); + gethostname (options->hostname, sizeof (options->hostname) - 1); + options->hostname[sizeof (options->hostname) - 1] = '\0'; + if (check_domain_name(options->hostname, strlen(options->hostname), 0) != 0 || strcmp (options->hostname, "localhost") == 0) memset (options->hostname, 0, sizeof (options->hostname)); @@ -232,6 +234,9 @@ int main(int argc, char **argv) "`%s' too long for HostName string, max is %d", optarg, MAXHOSTNAMELEN); goto abort; + } else if(check_domain_name(optarg, strlen(optarg), 0) != 0) { + logger (LOG_ERR, "suspect string in hostname argument"); + goto abort; } else strlcpy (options->hostname, optarg, sizeof (options->hostname)); ++++++ dhcpcd-3.2.3-renew-unicast.diff ++++++ --- dhcp.c +++ dhcp.c @@ -333,6 +333,22 @@ ssize_t send_message (const interface_t *iface, const dhcp_t *dhcp, message_length = p - m; + if (to.s_addr && to.s_addr != INADDR_BROADCAST) { + struct sockaddr_in sin_to; + + memset(&sin_to, 0, sizeof(sin_to)); + sin_to.sin_family = AF_INET; + sin_to.sin_addr.s_addr = to.s_addr; + sin_to.sin_port = htons(DHCP_SERVER_PORT); + + logger (LOG_DEBUG, "sending %s with xid 0x%x to %s", + dhcp_message (type), xid, inet_ntoa(to)); + + return sendto(iface->listen_fd, + (unsigned char *) message, message_length, + 0, (struct sockaddr *)&sin_to, sizeof(sin_to)); + } + packet = xzalloc (sizeof (*packet)); make_dhcp_packet (packet, (unsigned char *) message, message_length, from, to); @@ -340,6 +356,7 @@ ssize_t send_message (const interface_t *iface, const dhcp_t *dhcp, logger (LOG_DEBUG, "sending %s with xid 0x%x", dhcp_message (type), xid); + retval = send_packet (iface, ETHERTYPE_IP, (unsigned char *) packet, message_length + sizeof (packet->ip) + sizeof (packet->udp)); --- socket.c +++ socket.c @@ -461,8 +461,8 @@ int open_socket (interface_t *iface, int protocol) /* We need to bind to a port, otherwise Linux generate ICMP messages * that cannot contect the port when we have an address. - * We don't actually use this fd at all, instead using our packet - * filter socket. */ + * We actually use this fd for unicast messages only, otherwise we're + * using our packet filter socket. So read it as udp_fd ... */ if (iface->listen_fd == -1 && protocol == ETHERTYPE_IP) { if ((fd = socket (PF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { logger (LOG_ERR, "socket: %s", strerror (errno)); ++++++ dhcpcd-3.2.3-xen-checksum.diff ++++++ --- socket.c +++ socket.c @@ -48,7 +48,8 @@ # include <net/bpf.h> #elif __linux__ # include <linux/filter.h> -# include <netpacket/packet.h> +# include <linux/if_packet.h> +/* # include <netpacket/packet.h> */ # define bpf_insn sock_filter #endif @@ -202,7 +203,7 @@ void make_dhcp_packet(struct udp_dhcp_packet *packet, ip->ip_sum = checksum ((unsigned char *) ip, sizeof (*ip)); } -static int valid_dhcp_packet (unsigned char *data) +static int valid_dhcp_packet (unsigned char *data, int nocsum) { union { @@ -242,7 +243,7 @@ static int valid_dhcp_packet (unsigned char *data) memcpy (&d.packet->ip.ip_src, &source, sizeof (d.packet->ip.ip_src)); memcpy (&d.packet->ip.ip_dst, &dest, sizeof (d.packet->ip.ip_dst)); d.packet->ip.ip_len = d.packet->udp.uh_ulen; - if (udpsum && udpsum != checksum (d.data, bytes)) { + if (!nocsum && udpsum && udpsum != checksum (d.data, bytes)) { logger (LOG_ERR, "bad UDP checksum, ignoring"); retval = -1; } @@ -409,7 +410,7 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, memcpy (data, payload, len); have_data = true; } else { - if (valid_dhcp_packet (payload) >= 0) { + if (valid_dhcp_packet (payload, 0) >= 0) { union { unsigned char *buffer; @@ -535,6 +536,16 @@ int open_socket (interface_t *iface, int protocol) return (-1); } + n = 1; + if (setsockopt (fd, SOL_PACKET, PACKET_AUXDATA, &n, sizeof(n)) < 0) { + if (errno != ENOPROTOOPT) { + logger (LOG_ERR, "failed to set auxiliary packet data: %s", + strerror (errno)); + close (fd); + return (-1); + } + } + if (iface->fd > -1) close (iface->fd); iface->fd = fd; @@ -594,13 +605,26 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, unsigned char *buffer; struct udp_dhcp_packet *packet; } pay; + unsigned char cmsgbuf[CMSG_LEN(sizeof(struct tpacket_auxdata))]; + struct iovec iov = { + .iov_base = buffer, + .iov_len = iface->buffer_length, + }; + struct msghdr msg = { + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = cmsgbuf, + .msg_controllen = sizeof(cmsgbuf), + }; + struct cmsghdr *cmsg; + int nocsum = 0; /* We don't use the given buffer, but we need to rewind the position */ *buffer_pos = 0; memset (buffer, 0, iface->buffer_length); - bytes = read (iface->fd, buffer, iface->buffer_length); + bytes = recvmsg (iface->fd, &msg, 0); if (bytes == -1) { struct timespec ts; logger (LOG_ERR, "read: %s", strerror (errno)); @@ -609,6 +633,13 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, nanosleep (&ts, NULL); return (-1); } + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_PACKET && + cmsg->cmsg_type == PACKET_AUXDATA) { + struct tpacket_auxdata *aux = (void *)CMSG_DATA(cmsg); + nocsum = aux->tp_status & TP_STATUS_CSUMNOTREADY; + } + } *buffer_len = bytes; /* If it's an ARP reply, then just send it back */ @@ -630,7 +661,7 @@ ssize_t get_packet (const interface_t *iface, unsigned char *data, return (-1); } - if (valid_dhcp_packet (buffer) == -1) + if (valid_dhcp_packet (buffer, nocsum) == -1) return (-1); bytes = ntohs (pay.packet->ip.ip_len) - ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Remember to have fun... -- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
