File dhcp-3.1.1-dhclient-option-checks.bnc675052.diff of Package dhcp
Index: dhcp-3.1.1/client/dhclient.c
===================================================================
--- dhcp-3.1.1.orig/client/dhclient.c
+++ dhcp-3.1.1/client/dhclient.c
@@ -37,6 +37,8 @@ static char ocopyright[] =
#include "dhcpd.h"
#include "version.h"
+#include <stddef.h>
+#include <ctype.h>
TIME default_lease_time = 43200; /* 12 hours... */
TIME max_lease_time = 86400; /* 24 hours... */
@@ -76,6 +78,10 @@ int quiet=0;
int nowait=0;
static void usage PROTO ((void));
+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 (argc, argv, envp)
int argc;
@@ -197,8 +203,11 @@ int main (argc, argv, envp)
log_error("-H option host-name string \"%s\" is too long:"
"maximum length is %d characters", argv[i], HOST_NAME_MAX);
exit(1);
+ } else if (check_domain_name(argv [i], strlen(argv [i]), 0) != 0) {
+ log_error("suspect hostname in -H option - discarded");
+ } else {
+ dhclient_hostname = argv [i];
}
- dhclient_hostname = argv [i];
} else if (!strcmp (argv [i], "-nw")) {
nowait = 1;
} else if (!strcmp (argv [i], "-n")) {
@@ -2543,12 +2552,21 @@ void client_option_envadd (struct option
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)));
+ const char *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);
}
}
@@ -2621,12 +2639,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",
+ "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,
@@ -3256,3 +3293,103 @@ isc_result_t client_dns_update (struct c
data_string_forget (&ddns_dhcid, MDL);
return rcode;
}
+
+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 && 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;
+}
+
+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 || !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;
+}
+
+static int check_option_values( struct universe *universe,
+ unsigned int opt,
+ const char *ptr,
+ size_t len)
+{
+ if( !ptr)
+ 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 && len-- > 0; ptr++) {
+ if( !(isalnum((unsigned char)*ptr) ||
+ *ptr == '#' || *ptr == '%' ||
+ *ptr == '+' || *ptr == '-' ||
+ *ptr == '_' || *ptr == ':' ||
+ *ptr == '.' || *ptr == ',' ||
+ *ptr == '@' || *ptr == '~' ||
+ *ptr == '\\' || *ptr == '/' ||
+ *ptr == ' '))
+ return -1;
+ }
+ return 0;
+ break;
+ }
+ }
+ return 0;
+}
+
Index: dhcp-3.1.1/common/options.c
===================================================================
--- dhcp-3.1.1.orig/common/options.c
+++ dhcp-3.1.1/common/options.c
@@ -2934,7 +2934,8 @@ pretty_escape(char **dst, char *dend, co
count += 4;
}
} else if (**src == '"' || **src == '\'' || **src == '$' ||
- **src == '`' || **src == '\\') {
+ **src == '`' || **src == '\\' || **src == '|' ||
+ **src == '&' || **src == ';') {
if (*dst + 2 > dend)
return -1;
Index: dhcp-3.1.1/client/scripts/linux
===================================================================
--- dhcp-3.1.1.orig/client/scripts/linux
+++ dhcp-3.1.1/client/scripts/linux
@@ -267,18 +267,29 @@ if [ x$reason = xBOUND ] || [ x$reason =
/etc/sysconfig/network/ifcfg-${interface} 2>/dev/null`
fi
if [ "$DHCLIENT_SET_HOSTNAME" = yes ] ; then
+ rx_host='^[[:alnum:]][[:alnum:]_-]{0,62}$'
+
+ 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${new_host_name%%.*}" ]; then
- if [ "x${new_host_name%%.*}" != x ]; then
- hostname "${new_host_name%%.*}"
+ current_hostname="${current_hostname%%.*}"
+ [[ ${current_hostname} =~ ${rx_host} ]] || unset current_hostname
+
+ 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:\..*::'`"
- if [ "x${_hostname}" != x ]; then
+ _hostname="`echo "$out" | sed 's:^.* ::; s:\..*::; s:.*[)]::'`"
+ [[ ${_hostname} =~ ${rx_host} ]] || unset _hostname
+ if [ "x${_hostname}" != "x" -a \
+ "x${_hostname}" != "x${current_hostname}" ]; then
hostname "${_hostname}"
fi
fi