Bug 3979

Summary: udhcpc should filter out malicious hostnames passed in option 0x0c
Product: Busybox Reporter: Denys Vlasenko <vda.linux>
Component: OtherAssignee: Bernhard Reutner-Fischer <aldot>
Status: RESOLVED FIXED    
Severity: critical CC: busybox-cvs, danny, mjt+busybox
Priority: P5    
Version: unspecified   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
Host: Target:
Build:
Attachments: udhcpc: check validity of hostname
Proposed patch
CentOS 5.5 dhclient-script
CentOS 6.4 dhclient-script

Description Denys Vlasenko 2011-07-15 10:04:15 UTC
In particular, control chars, high-bit-set chars and, ', `, $ may be used to do nasty things.

The official rule is:

"The characters allowed in a label [DNS component] are a-z, A-Z, 0-9, and the hyphen. Labels may not start or end with a hyphen."

See similar fix: https://bugzilla.redhat.com/show_bug.cgi?id=689832
Comment 1 Denys Vlasenko 2011-07-15 10:14:55 UTC
We don't need to be particularly anal. For example, allowing _, hyphen at the end, or leading and trailing dots would be ok, since it can't be used for attacks. (Leading hyphen can be, if someone uses

cmd "$hostname"

in the script: then hostname may be treated as an option)
Comment 2 Bernhard Reutner-Fischer 2011-12-05 08:59:25 UTC
Created attachment 3842 [details]
udhcpc: check validity of hostname

make sure hostname is a valid domain name
Comment 3 Bernhard Reutner-Fischer 2011-12-05 09:00:33 UTC
Not sure if you are aware of that but there is a security advisory due to this bug: http://secunia.com/advisories/45363/
Comment 4 Bernhard Reutner-Fischer 2011-12-05 09:15:49 UTC
(In reply to comment #3)
> Not sure if you are aware of that but there is a security advisory due to this
> bug: http://secunia.com/advisories/45363/

Along the same lines, i searched for but cannot find security advisories against everybody implementing or using unlink(2) but would have assumed there are advisories because "rm" is really, really evil, too.
Comment 5 Denys Vlasenko 2011-12-08 15:40:36 UTC
Created attachment 3860 [details]
Proposed patch

This patch doesn't check all invalid hostnames. It only prevents ones with non-ascii, punctuation, with leading or repeated periods, and with leading dashes. +43 bytes.
Comment 6 Denys Vlasenko 2011-12-08 15:43:23 UTC
Fixed in git:

commit 7280d2017d8075267a12e469983e38277dcf0374
Author: Denys Vlasenko <vda.linux@googlemail.com>
Date:   Thu Dec 8 16:41:05 2011 +0100

    udhcpc: sanitize hostnames in incoming packets. Closes 3979.

    The following options are replaced with string "bad" if they
    contain malformed hostname:
    HOST_NAME, DOMAIN_NAME, NIS_DOMAIN, TFTP_SERVER_NAME

    function                                             old     new   delta
    xmalloc_optname_optval                               850     888     +38
    attach_option                                        440     443      +3
    len_of_option_as_string                               13      14      +1
    dhcp_option_lengths                                   13      14      +1
Comment 7 Denys Vlasenko 2011-12-08 15:44:42 UTC
(In reply to comment #2)
> Created attachment 3842 [details]
> udhcpc: check validity of hostname
> 
> make sure hostname is a valid domain name

The bug is about sanitizing hostnames in incoming packets. We don't interfere if local admin uses bogus hostname in -h HOST option...
Comment 8 danny 2014-04-07 15:07:04 UTC
Hi,

Sorry to beat dead horse, but commit 7280d2017d8075267a12e469983e38277dcf0374 effectively broke udhcpc, so now it refuse to work with multiple domains in "search" line, replacing all of them by single word "bad". 
IMHO, you should not touch DOMAIN_NAME if it not contains really bad characters. As said earlier - it should not be such anal ;)


Our environment has 5 different sites, and we populating resolv.conf by such DHCP settings:

option domain-name "siteX.sub.domain sub.domain domain";
option domain-name-servers 10.2.1.1, 10.7.1.2, 10.21.1.2;
 
Such configuration is happily accepted by all Linux servers (and even Windows), leaving us with such stuff in /etc/resolv.conf:

search site1.sub.domain sub.domain domain
nameserver 10.2.1.1
nameserver 10.7.1.2
nameserver 10.21.1.2

But now it is not true for installers based on updated busybox - in my case it is Debian 7.2 x64:

search bad
nameserver 10.2.1.1
nameserver 10.7.1.2
nameserver 10.21.1.2

As possible workaround, I've got recommendations to use such DHCP config (and i was able to overcome that "bad" stuff):

option domain-name "siteX.sub.domain";
option domain-search "sub.domain","domain";
option domain-name-servers 10.2.1.1, 10.7.1.2, 10.21.1.2;

But such config bring other bad things and incompatibilities:

; generated by /sbin/dhclient-script
search sub.domain. domain. 
nameserver 10.2.1.1
nameserver 10.7.1.2
nameserver 10.21.1.2


Please advice.

Thank you!
Comment 9 Denys Vlasenko 2014-04-16 15:33:37 UTC
(In reply to comment #8)
> Hi,
> 
> Sorry to beat dead horse, but commit 7280d2017d8075267a12e469983e38277dcf0374
> effectively broke udhcpc, so now it refuse to work with multiple domains in
> "search" line, replacing all of them by single word "bad". 
> IMHO, you should not touch DOMAIN_NAME if it not contains really bad
> characters. As said earlier - it should not be such anal ;)
> 
> 
> Our environment has 5 different sites, and we populating resolv.conf by such
> DHCP settings:
> 
> option domain-name "siteX.sub.domain sub.domain domain";
> option domain-name-servers 10.2.1.1, 10.7.1.2, 10.21.1.2;
> 
> Such configuration is happily accepted by all Linux servers (and even Windows),
> leaving us with such stuff in /etc/resolv.conf:
> 
> search site1.sub.domain sub.domain domain
> nameserver 10.2.1.1
> nameserver 10.7.1.2
> nameserver 10.21.1.2

This is an abuse of "domain" option to contain a list of search domains instead.

Despite it being accepted, this config is wrong: it says that your machine belongs to
"siteX.sub.domain sub.domain domain" domain, which is obviously wrong.

Some systems put this string into /etc/resolv.conf's "domain" directive.
"man resolv.conf" says:

"""
       domain <Local domain name>
              Most queries for names within this domain can use short names relative to the local domain.
"""

> But now it is not true for installers based on updated busybox - in my case it
> is Debian 7.2 x64:
> 
> search bad
> nameserver 10.2.1.1
> nameserver 10.7.1.2
> nameserver 10.21.1.2

> As possible workaround, I've got recommendations to use such DHCP config (and i
> was able to overcome that "bad" stuff):
> 
> option domain-name "siteX.sub.domain";
> option domain-search "sub.domain","domain";
> option domain-name-servers 10.2.1.1, 10.7.1.2, 10.21.1.2;

This looks correct: it uses correct option to pass list of search domains.

> But such config bring other bad things and incompatibilities:
> 
> ; generated by /sbin/dhclient-script
> search sub.domain. domain. 
> nameserver 10.2.1.1
> nameserver 10.7.1.2
> nameserver 10.21.1.2

Please attach your /sbin/dhclient-script and tcpdump capture of DHCP reply packet which carries the data.
Comment 10 danny 2014-04-16 16:11:07 UTC
Hi,

... SKIP ...

> > search site1.sub.domain sub.domain domain
> > nameserver 10.2.1.1
> > nameserver 10.7.1.2
> > nameserver 10.21.1.2
> 
> This is an abuse of "domain" option to contain a list of search domains
> instead.

It will be abuse if I use "domain" option. But we are not talking about "domain" here, we are talking about "search" (which is different from domain):

From http://linux.die.net/man/5/resolv.conf:

domain Local domain name.

    Most queries for names within this domain can use short names relative to the local domain. If no domain entry is present, the domain is determined from the local hostname returned by gethostname(2); the domain part is taken to be everything after the first '.'. Finally, if the hostname does not contain a domain part, the root domain is assumed. 

search Search list for host-name lookup.

    The search list is normally determined from the local domain name; by default, it contains only the local domain name. This may be changed by listing the desired domain search path following the search keyword with spaces or tabs separating the names. 
                        ... SKIP ...
   The search list is currently limited to six domains with a total of 256 characters. 


> 
> Despite it being accepted, this config is wrong: it says that your machine
> belongs to
> "siteX.sub.domain sub.domain domain" domain, which is obviously wrong.
> 

It will be wrong if I add them to "domain" option, but it added to "search", so nothing wrong.


Thanks,
D.
Comment 11 Denys Vlasenko 2014-04-16 17:39:57 UTC
(In reply to comment #10)
> > > search site1.sub.domain sub.domain domain
> > > nameserver 10.2.1.1
> > > nameserver 10.7.1.2
> > > nameserver 10.21.1.2
> > 
> > This is an abuse of "domain" option to contain a list of search domains
> > instead.
> 
> It will be abuse if I use "domain" option. But we are not talking about
> "domain" here, we are talking about "search" (which is different from domain):
> 
> From http://linux.die.net/man/5/resolv.conf:

No, we are not talking about "search".

DHCP options do not match one-to-one to resolv.conf directives.
DHCP protocol per se knows nothing about resolv.conf.

If your DHCP server has configured to use "option domain-name "SOMETHING";"
it is clear it will send packets with DHCP option 15 (or 0x0f, if you prefer).

http://www.networksorcery.com/enp/rfc/rfc2132.txt says this about option 15:

"""
3.17. Domain Name

   This option specifies the domain name that client should use when
   resolving hostnames via the Domain Name System.

   The code for this option is 15.  Its minimum length is 1.
"""

RFC does not allow _a list of domains_ there. Only one domain.


> It will be wrong if I add them to "domain" option, but it added to "search", so nothing wrong.

You should not use option 15 to pass a list of search domains, otherwise you can discover
that tools which are RFC-2132 compliant won't agree to process it.

There are options which are intended to pass lists of search domains. Use *them*.

Please attach your /sbin/dhclient-script and tcpdump capture of DHCP reply
packet which carries the data.
Comment 12 danny 2014-04-17 13:53:49 UTC
Created attachment 5330 [details]
CentOS 5.5 dhclient-script
Comment 13 danny 2014-04-17 13:55:04 UTC
Created attachment 5336 [details]
CentOS 6.4 dhclient-script
Comment 14 danny 2014-04-17 14:30:49 UTC
> You should not use option 15 to pass a list of search domains, otherwise you
> can discover
> that tools which are RFC-2132 compliant won't agree to process it.
> 

I understand your good intention, but you should not break something, that has
been working for years (correct or incorrect - it was doing his job as
expected).

Ok, now it is "correct", but does not works anymore. Who need such fix?

Did not had any problems with any tools for last 3 years... 
Probably, I don't use/know them... Can you point on such tools?

> There are options which are intended to pass lists of search domains. Use
> *them*.

How about old systems, whose dhclient does not know about *those* options?
(upgrade is not an option)

> 
> Please attach your /sbin/dhclient-script and tcpdump capture of DHCP reply
> packet which carries the data.

dhclient-script from CentOS 5.5 and 6.4 are attached. Can't provide you with tcpdump yet, but you already know what is there.
Comment 15 danny 2014-04-17 16:20:58 UTC
Such dhcpd config (irrelevant lines are skipped):

nis-domain "some"; 
subnet-mask 255.255.0.0; 
broadcast-address 172.*.255; 
domain-name "siteX.sub.domain"; 
domain-search "siteX.sub.domain","sub.domain","domain","other.sub.domain","other.domain";
domain-name-servers 10.*,10.*,10.*,10.*; 
ntp-servers 10.*; 
routers 172.*;

Generates following leases on different OS:

from CentOS 6.x:

lease {
  interface "eth0";
  fixed-address 172.*;
  filename "pxelinux.0";
  option subnet-mask 255.255.0.0;
  option routers 172.*;
  option dhcp-lease-time 36000;
  option dhcp-message-type 5;
  option domain-name-servers 10.*,10.*,10.*,10.*;
  option dhcp-server-identifier 10.*;
  option domain-search "siteX.sub.domain.", "sub.domain.", "domain.", "other.sub.domain.", "other.domain.";
  option nis-domain "some";
  option nis-servers 10.*;
  option ntp-servers 10.*;
  option broadcast-address 172.*.255;
  option host-name "some21";
  option domain-name "siteX.sub.domain";
  renew 4 2014/04/17 18:31:38;
  rebind 4 2014/04/17 23:19:29;
  expire 5 2014/04/18 00:34:29;
}


from CentOS 5.x:

lease {
  interface "eth0";
  fixed-address 172.*;
  filename "pxelinux.0";
  option subnet-mask 255.255.0.0;
  option routers 10.*;
  option dhcp-lease-time 1209600;
  option dhcp-message-type 5;
  option domain-name-servers 10.*,10.*,10.*;
  option dhcp-server-identifier 10.*;
  option nis-domain "some";
  option nis-servers 10.*;
  option ntp-servers 10.*;
  option broadcast-address 172.*.255;
  option host-name "some21";
  option domain-name "siteX.sub.domain";
  renew 3 2014/04/23 03:08:45;
  rebind 2 2014/04/29 21:56:57;
  expire 4 2014/05/01 15:56:57;
}

As you can see, 5.x stuff even not asking for domain-search stuff.

Moreover, this command should bring needed, but it won't:

# dhclient -R subnet-mask,broadcast-address,routers,domain-name,domain-name-servers,host-name,nis-domain,nis-servers,domain-search eth0

lease {
  interface "eth0";
  fixed-address 172.*;
  filename "pxelinux.0";
  option subnet-mask 255.255.0.0;
  option routers 10.*;
  option dhcp-lease-time 1209600;
  option dhcp-message-type 5;
  option domain-name-servers 10.*,10.*,10.*;
  option dhcp-server-identifier 10.*;
  option domain-search ;    <=========== empty...
  option nis-domain "some";
  option nis-servers 10.*;
  option ntp-servers 10.*;
  option broadcast-address 172.*.255;
  option host-name "some21";
  option domain-name "siteX.sub.domain";
  renew 3 2014/04/23 21:41:26;
  rebind 2 2014/04/29 21:52:11;
  expire 4 2014/05/01 15:52:11;
}

And, even we add following to dhcpd config, it still ignored all together in 5.x:

option domain-forced-list code 119 = string;
domain-forced-list "siteX.sub.domain","sub.domain","domain","other.sub.domain","other.domain";

So, following advices to use "right options", we hitting even more problems and incompatibilities in different OS.
Comment 16 Denys Vlasenko 2014-04-18 18:50:01 UTC
(In reply to comment #15)
> Such dhcpd config (irrelevant lines are skipped):
> 
> nis-domain "some"; 
> subnet-mask 255.255.0.0; 
> broadcast-address 172.*.255; 
> domain-name "siteX.sub.domain"; 
> domain-search
> "siteX.sub.domain","sub.domain","domain","other.sub.domain","other.domain";
> domain-name-servers 10.*,10.*,10.*,10.*; 
> ntp-servers 10.*; 
> routers 172.*;
> 
> Generates following leases on different OS:
> 
> from CentOS 6.x:
> 
> lease {
>   interface "eth0";
>   fixed-address 172.*;
>   filename "pxelinux.0";
>   option subnet-mask 255.255.0.0;
>   option routers 172.*;
>   option dhcp-lease-time 36000;
>   option dhcp-message-type 5;
>   option domain-name-servers 10.*,10.*,10.*,10.*;
>   option dhcp-server-identifier 10.*;
>   option domain-search "siteX.sub.domain.", "sub.domain.", "domain.",
> "other.sub.domain.", "other.domain.";

Seems to work here.
Trailing dot looks wrong, you may want to file a bug against this client software.

> As you can see, 5.x stuff even not asking for domain-search stuff.

Yes, it probably doesn't support that.


> And, even we add following to dhcpd config, it still ignored all together in
> 5.x:
> 
> option domain-forced-list code 119 = string;
> domain-forced-list
> "siteX.sub.domain","sub.domain","domain","other.sub.domain","other.domain";

Option 119 is not a string option, it uses "\003foo\004blah\003com\000" encoding (RFC 1035).

> So, following advices to use "right options", we hitting even more problems and incompatibilities in different OS.

Because many clients have bugs in handling more recently introduced options. Such is life. Bugs needs to be filed in bugzillas to get them fixed.


> Can't provide you with tcpdump yet, but you already know what is there.

I do want tcpdump, because I in fact don't know what _exactly_ is there. For example, trailing dot problem can exist in bbox's DHCP client, udhcpc, and in order to test it, I want to see a real-world example of the packed, instead of assuming what's there.
Comment 17 danny 2014-04-20 10:31:29 UTC
Hi,

> Option 119 is not a string option, it uses "\003foo\004blah\003com\000"
> encoding (RFC 1035).
> 
> > So, following advices to use "right options", we hitting even more problems and incompatibilities in different OS.

Everywhere in man's it written as "string"...

> 
> Because many clients have bugs in handling more recently introduced options.
> Such is life. Bugs needs to be filed in bugzillas to get them fixed.
> 

Here you too optimistic. Nobody bother to fix old stuff, especially on old systems...

> 
> > Can't provide you with tcpdump yet, but you already know what is there.
> 
> I do want tcpdump, because I in fact don't know what _exactly_ is there. For
> example, trailing dot problem can exist in bbox's DHCP client, udhcpc, and in
> order to test it, I want to see a real-world example of the packed, instead of
> assuming what's there.

Sent you by email.

Anyway, there is a way, which can be acceptable by all parties:

if (ch == '\0' || ch == ' ' || ch == '.' )
                               return label;

Here you will get domain name in any case + ' ' is not evil + RFC conformant + nothing broken.
Comment 18 danny 2014-05-19 16:10:17 UTC
(In reply to comment #17)
> Hi,
> 
> > Option 119 is not a string option, it uses "\003foo\004blah\003com\000"
> > encoding (RFC 1035).
> > 
> > > So, following advices to use "right options", we hitting even more problems and incompatibilities in different OS.
> 
> Everywhere in man's it written as "string"...
> 
> > 
> > Because many clients have bugs in handling more recently introduced options.
> > Such is life. Bugs needs to be filed in bugzillas to get them fixed.
> > 
> 
> Here you too optimistic. Nobody bother to fix old stuff, especially on old
> systems...
> 
> > 
> > > Can't provide you with tcpdump yet, but you already know what is there.
> > 
> > I do want tcpdump, because I in fact don't know what _exactly_ is there. For
> > example, trailing dot problem can exist in bbox's DHCP client, udhcpc, and in
> > order to test it, I want to see a real-world example of the packed, instead of
> > assuming what's there.
> 
> Sent you by email.
> 
> Anyway, there is a way, which can be acceptable by all parties:
> 
> if (ch == '\0' || ch == ' ' || ch == '.' )
>                                return label;
> 
> Here you will get domain name in any case + ' ' is not evil + RFC conformant +
> nothing broken.

Hi again,

Any acknowledgement?

Thanks,
D.
Comment 19 danny 2014-06-12 15:41:48 UTC
Hi,

Not replying looks childish and unprofessional.
You can't ignore what users has to say.

I'm looking forward to escalate this case to project owner.

Best regards,
Danny
Comment 20 Denys Vlasenko 2014-06-15 22:19:41 UTC
(In reply to comment #17)
> Hi,
> 
> > Option 119 is not a string option, it uses "\003foo\004blah\003com\000"
> > encoding (RFC 1035).
> > 
> > > So, following advices to use "right options", we hitting even more problems and incompatibilities in different OS.
> 
> Everywhere in man's it written as "string"...
> 
> > 
> > Because many clients have bugs in handling more recently introduced options.
> > Such is life. Bugs needs to be filed in bugzillas to get them fixed.
> > 
> 
> Here you too optimistic. Nobody bother to fix old stuff, especially on old
> systems...
> 
> > 
> > > Can't provide you with tcpdump yet, but you already know what is there.
> > 
> > I do want tcpdump, because I in fact don't know what _exactly_ is there. For
> > example, trailing dot problem can exist in bbox's DHCP client, udhcpc, and in
> > order to test it, I want to see a real-world example of the packed, instead of
> > assuming what's there.
> 
> Sent you by email.
> 
> Anyway, there is a way, which can be acceptable by all parties:
> 
> if (ch == '\0' || ch == ' ' || ch == '.' )
>                                return label;
> 
> Here you will get domain name in any case + ' ' is not evil + RFC conformant +
> nothing broken.

A better (for some definition of "better") solution
is to make hostname sanitization configurable.

Then you can turn off it and continue to (ab)use wrong option,
whereas security-obsessed people can be happy too.

I committed this change to git:

http://git.busybox.net/busybox/commit/?id=85090c162b322a4ffe53d251e59bbfc212a829ee