SHA256
1
0
forked from pool/dhcp

- 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
  (bnc#675052, CVE-2011-0997).

OBS-URL: https://build.opensuse.org/package/show/network:dhcp/dhcp?expand=0&rev=62
This commit is contained in:
Marius Tomaschewski 2011-04-06 09:08:11 +00:00 committed by Git OBS Bridge
parent c3435bba35
commit 43cf8a5665
4 changed files with 274 additions and 9 deletions

View File

@ -236,21 +236,26 @@ set_ipv6_routes()
}
set_hostname()
{
rx_host='^[[:alnum:]][[:alnum:]_-]{0,62}$'
if [ "$DHCLIENT_SET_HOSTNAME" = yes ] ; then
new_host_name="${new_host_name%%.*}"
[[ ${new_host_name} =~ ${rx_host} ]] || unset new_host_name
current_hostname=`hostname`
if [ "x${current_hostname%%.*}" = "x" ] || \
[ "x${current_hostname%%.*}" = "x(none)" ] || \
[ "x${current_hostname%%.*}" = "xlocalhost" ] || \
[ "x${current_hostname%%.*}" != "x${new_host_name%%.*}" ]; then
current_hostname="${current_hostname%%.*}"
[[ ${current_hostname} =~ ${rx_host} ]] || unset current_hostname
if [ "x${new_host_name%%.*}" != "x" ]; then
hostname "${new_host_name%%.*}"
if [ "x${current_hostname}" = "x" ] || \
[ "x${current_hostname}" = "xlocalhost" ] || \
[ "x${current_hostname}" != "x${new_host_name}" ]; then
if [ "x${new_host_name}" != "x" ]; then
hostname "${new_host_name}"
else
if [ -x /usr/bin/host ] ; then
if out=`host -W 2 "$new_ip_address" 2>/dev/null` ; then
_hostname="`echo "$out" | sed 's:^.* ::; s:\..*::'`"
_hostname="`echo "$out" | sed 's:^.* ::; s:\..*::; s:.*[)]::'`"
[[ ${_hostname} =~ ${rx_host} ]] || unset _hostname
if [ "x${_hostname}" != "x" -a \
"x${_hostname}" != "x${current_hostname%%.*}" ]; then
"x${_hostname}" != "x${current_hostname}" ]; then
hostname "${_hostname}"
fi
fi
@ -264,7 +269,9 @@ set_hostname()
# it changed, we've to handle it anyway...
local OLD_HOSTNAME=`read_cached_config_data hostname $interface`
local CUR_HOSTNAME=`hostname 2>/dev/null`
if test "x$OLD_HOSTNAME" != "x$CUR_HOSTNAME" ; then
CUR_HOSTNAME="${CUR_HOSTNAME%%.*}"
if [[ ${CUR_HOSTNAME} =~ ${rx_host} ]] && \
[ "x$OLD_HOSTNAME" != "x$CUR_HOSTNAME" ] ; then
write_cached_config_data hostname "$CUR_HOSTNAME" $interface
commit_cached_config_data $interface

View File

@ -0,0 +1,248 @@
From 632c8ceeff26a7663f939895f77aecb8377773f2 Mon Sep 17 00:00:00 2001
From: Marius Tomaschewski <mt@suse.de>
Date: Sun, 27 Mar 2011 13:15:58 +0200
Subject: [PATCH] dhclient: discard incorrect string options
Discard string options such as host and domain names
containing disallowed characters or beeing too long.
This proctive patch limits root-path to the a-zA-Z0-9,
space and the #%+-_:.,@~/\[]= characters.
Signed-off-by: Marius Tomaschewski <mt@suse.de>
---
client/dhclient.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++----
common/options.c | 3 +-
2 files changed, 175 insertions(+), 14 deletions(-)
diff --git a/client/dhclient.c b/client/dhclient.c
index dc19e8b..5d96c72 100644
--- a/client/dhclient.c
+++ b/client/dhclient.c
@@ -91,6 +91,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;
@@ -3034,13 +3039,23 @@ void client_option_envadd (struct option_cache *oc,
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);
}
}
@@ -3118,12 +3133,32 @@ void script_write_params (client, prefix, lease)
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",
+ "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",
+ "server_name");
+ }
+ }
+
for (i = 0; i < lease -> options -> universe_count; i++) {
option_space_foreach ((struct packet *)0, (struct lease *)0,
@@ -4026,3 +4061,128 @@ 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 --git a/common/options.c b/common/options.c
index 28c36e6..3a6cb33 100644
--- a/common/options.c
+++ b/common/options.c
@@ -3915,7 +3915,8 @@ pretty_escape(char **dst, char *dend, const unsigned char **src,
count += 4;
}
} else if (**src == '"' || **src == '\'' || **src == '$' ||
- **src == '`' || **src == '\\') {
+ **src == '`' || **src == '\\' || **src == '|' ||
+ **src == '&' || **src == ';') {
if (*dst + 2 > dend)
return -1;
--
1.7.3.4

View File

@ -1,3 +1,11 @@
-------------------------------------------------------------------
Thu Mar 31 09:56:02 UTC 2011 - mt@suse.de
- 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
(bnc#675052, CVE-2011-0997).
-------------------------------------------------------------------
Thu Mar 31 09:00:19 UTC 2011 - mt@suse.de

View File

@ -82,6 +82,7 @@ Patch30: dhcp-4.2.1-ldap-patch-mt01.diff.bz2
Patch40: dhcp-4.1.1-P1-lpf-bind-msg-fix.diff
Patch41: dhcp-4.1.1-P1-relay-no-ip-on-interface.diff
Patch44: dhcp-4.2.0-xen-checksum.patch
Patch45: dhcp-4.2.1-dhclient-option-checks.bnc675052.diff
##
PreReq: /bin/touch /sbin/chkconfig sysconfig
BuildRoot: %{_tmppath}/%{name}-%{version}-build
@ -206,6 +207,7 @@ Authors:
%patch40 -p1
%patch41 -p1
%patch44 -p1
%patch45 -p1
##
find . -type f -name \*.cat\* -exec rm -f {} \;
dos2unix contrib/ms2isc/*