[dhcp/f14/master] CVE-2011-0997 dhclient: insufficient sanitization of certain DHCP response values (#694005)
Jiří Popelka
jpopelka at fedoraproject.org
Wed Apr 6 10:32:20 UTC 2011
commit 83d520f457958a6819d2a710c76cfc66b83c7927
Author: Jiri Popelka <jpopelka at redhat.com>
Date: Wed Apr 6 12:12:04 2011 +0200
CVE-2011-0997 dhclient: insufficient sanitization of certain DHCP response values (#694005)
dhcp-4.2.0-P2-CVE-2011-0997.patch | 223 +++++++++++++++++++++++++++++++++++++
dhcp.spec | 11 ++-
2 files changed, 233 insertions(+), 1 deletions(-)
---
diff --git a/dhcp-4.2.0-P2-CVE-2011-0997.patch b/dhcp-4.2.0-P2-CVE-2011-0997.patch
new file mode 100644
index 0000000..f8ad4f1
--- /dev/null
+++ b/dhcp-4.2.0-P2-CVE-2011-0997.patch
@@ -0,0 +1,223 @@
+diff -up dhcp-4.2.0-P2/client/dhclient.c.CVE-2011-0997 dhcp-4.2.0-P2/client/dhclient.c
+--- dhcp-4.2.0-P2/client/dhclient.c.CVE-2011-0997 2011-04-06 11:57:13.000000000 +0200
++++ dhcp-4.2.0-P2/client/dhclient.c 2011-04-06 12:03:31.000000000 +0200
+@@ -105,6 +105,11 @@ static void usage(void);
+
+ static isc_result_t write_duid(struct data_string *duid);
+
++static int check_domain_name(const char *ptr, size_t len, int dots);
++static int check_domain_name_list(const char *ptr, size_t len, int dots);
++static int check_option_values(struct universe *universe, unsigned int opt,
++ const char *ptr, size_t len);
++
+ int
+ main(int argc, char **argv) {
+ int fd;
+@@ -3430,13 +3435,23 @@ void client_option_envadd (struct option
+ if (data.len) {
+ char name [256];
+ if (dhcp_option_ev_name (name, sizeof name,
+- oc -> option)) {
+- client_envadd (es -> client, es -> prefix,
+- name, "%s",
+- (pretty_print_option
+- (oc -> option,
+- data.data, data.len,
+- 0, 0)));
++ oc->option)) {
++ const char *value;
++ value = pretty_print_option(oc->option,
++ data.data,
++ data.len, 0, 0);
++ size_t length = strlen(value);
++
++ if (check_option_values(oc->option->universe,
++ oc->option->code,
++ value, length) == 0) {
++ client_envadd(es->client, es->prefix,
++ name, "%s", value);
++ } else {
++ log_error("suspect value in %s "
++ "option - discarded",
++ name);
++ }
+ data_string_forget (&data, MDL);
+ }
+ }
+@@ -3514,12 +3529,31 @@ void script_write_params (client, prefix
+ data_string_forget (&data, MDL);
+ }
+
+- if (lease -> filename)
+- client_envadd (client,
+- prefix, "filename", "%s", lease -> filename);
+- if (lease -> server_name)
+- client_envadd (client, prefix, "server_name",
+- "%s", lease -> server_name);
++ if (lease->filename) {
++ if (check_option_values(NULL, DHO_ROOT_PATH,
++ lease->filename,
++ strlen(lease->filename)) == 0) {
++ client_envadd(client, prefix, "filename",
++ "%s", lease->filename);
++ } else {
++ log_error("suspect value in %s "
++ "option - discarded",
++ lease->filename);
++ }
++ }
++
++ if (lease->server_name) {
++ if (check_option_values(NULL, DHO_HOST_NAME,
++ lease->server_name,
++ strlen(lease->server_name)) == 0 ) {
++ client_envadd (client, prefix, "server_name",
++ "%s", lease->server_name);
++ } else {
++ log_error("suspect value in %s "
++ "option - discarded",
++ lease->server_name);
++ }
++ }
+
+ for (i = 0; i < lease -> options -> universe_count; i++) {
+ option_space_foreach ((struct packet *)0, (struct lease *)0,
+@@ -4429,3 +4463,125 @@ dhcpv4_client_assignments(void)
+ } else
+ remote_port = htons (ntohs (local_port) - 1); /* XXX */
+ }
++
++/*
++ * The following routines are used to check that certain
++ * strings are reasonable before we pass them to the scripts.
++ * This avoids some problems with scripts treating the strings
++ * as commands - see ticket 23722
++ * The domain checking code should be done as part of assembling
++ * the string but we are doing it here for now due to time
++ * constraints.
++ */
++
++static 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 != 0) && (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.') */
++ size_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) == 0) {
++ /* also numbers at the begin are fine */
++ return(-1);
++ }
++ }
++ return(dots ? -1 : 0);
++}
++
++static 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 == NULL) || (len == 0))
++ return(-1);
++
++ for (p=ptr; (*p != 0) && (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);
++}
++
++static int check_option_values(struct universe *universe,
++ unsigned int opt,
++ const char *ptr,
++ size_t len)
++{
++ if (ptr == NULL)
++ return(-1);
++
++ /* just reject options we want to protect, will be escaped anyway */
++ if ((universe == NULL) || (universe == &dhcp_universe)) {
++ switch(opt) {
++ case DHO_HOST_NAME:
++ case DHO_DOMAIN_NAME:
++ case DHO_NIS_DOMAIN:
++ case DHO_NETBIOS_SCOPE:
++ return check_domain_name(ptr, len, 0);
++ break;
++ case DHO_DOMAIN_SEARCH:
++ return check_domain_name_list(ptr, len, 0);
++ break;
++ case DHO_ROOT_PATH:
++ if (len == 0)
++ return(-1);
++ for (; (*ptr != 0) && (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;
++ }
++ }
++
++#ifdef DHCPv6
++ if (universe == &dhcpv6_universe) {
++ switch(opt) {
++ case D6O_SIP_SERVERS_DNS:
++ case D6O_DOMAIN_SEARCH:
++ case D6O_NIS_DOMAIN_NAME:
++ case D6O_NISP_DOMAIN_NAME:
++ return check_domain_name_list(ptr, len, 0);
++ break;
++ }
++ }
++#endif
++
++ return(0);
++}
+diff -up dhcp-4.2.0-P2/common/options.c.CVE-2011-0997 dhcp-4.2.0-P2/common/options.c
+--- dhcp-4.2.0-P2/common/options.c.CVE-2011-0997 2011-04-06 11:57:13.000000000 +0200
++++ dhcp-4.2.0-P2/common/options.c 2011-04-06 12:05:40.000000000 +0200
+@@ -3962,7 +3962,8 @@ pretty_escape(char **dst, char *dend, co
+ count += 4;
+ }
+ } else if (**src == '"' || **src == '\'' || **src == '$' ||
+- **src == '`' || **src == '\\') {
++ **src == '`' || **src == '\\' || **src == '|' ||
++ **src == '&') {
+ if (*dst + 2 > dend)
+ return -1;
+
diff --git a/dhcp.spec b/dhcp.spec
index ceeefd6..154996c 100644
--- a/dhcp.spec
+++ b/dhcp.spec
@@ -12,7 +12,7 @@
Summary: Dynamic host configuration protocol software
Name: dhcp
Version: 4.2.0
-Release: 19.%{patchver}%{?dist}
+Release: 20.%{patchver}%{?dist}
# NEVER CHANGE THE EPOCH on this package. The previous maintainer (prior to
# dcantrell maintaining the package) made incorrect use of the epoch and
# that's why it is at 12 now. It should have never been used, but it was.
@@ -66,6 +66,7 @@ Patch32: dhcp-4.2.0-PPP.patch
Patch33: dhcp-4.2.0-P2-omapi.patch
Patch34: dhcp-4.2.0-P2-ldap-configuration.patch
Patch35: dhcp-4.2.0-P2-CVE-2011-0413.patch
+Patch36: dhcp-4.2.0-P2-CVE-2011-0997.patch
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
BuildRequires: autoconf
@@ -273,6 +274,10 @@ libdhcpctl and libomapi static libraries are also included in this package.
# CVE-2011-0413: Unexpected abort caused by a DHCPv6 decline message (#672996)
%patch35 -p1 -b .CVE-2011-0413
+# CVE-2011-0997
+# dhclient: insufficient sanitization of certain DHCP response values (#694005)
+%patch36 -p1 -b .CVE-2011-0997
+
# Copy in the Fedora/RHEL dhclient script
%{__install} -p -m 0755 %{SOURCE4} client/scripts/linux
%{__install} -p -m 0644 %{SOURCE5} .
@@ -554,6 +559,10 @@ fi
%attr(0644,root,root) %{_mandir}/man3/omapi.3.gz
%changelog
+* Wed Apr 06 2011 Jiri Popelka <jpopelka at redhat.com> - 12:4.2.0-20.P2
+- CVE-2011-0997
+ dhclient: insufficient sanitization of certain DHCP response values (#694005)
+
* Thu Jan 27 2011 Jiri Popelka <jpopelka at redhat.com> - 12:4.2.0-19.P2
- CVE-2011-0413: Unexpected abort caused by a DHCPv6 decline message (#672996)
More information about the scm-commits
mailing list