xorl %eax, %eax

CVE-2011-1739: FreeBSD mountd(8) ACL Mishandling

leave a comment »

On 20 April 2011 the FreeBSD Project published a new security advisory. The issue affects mountd(8) and credits for this discovery go to Ruslan Ermilov.
From the official security advisory we can read this description for the issue.

While parsing the exports(5) table, a network mask in the form of
"-network=netname/prefixlength" results in an incorrect network mask
being computed if the prefix length is not a multiple of 8.

For example, specifying the ACL for an export as "-network 192.0.2.0/23"
would result in a netmask of 255.255.127.0 being used instead of the
correct netmask of 255.255.254.0.

Because of this design flaw the Access Control List (ACL) would not be applied to the appropriate hosts and thus unauthorized users could have access. The exact code resides in usr.sbin/mountd/mountd.c and more specifically in the below C function.

/*
 * Make a netmask according to the specified prefix length. The ss_family
 * and other non-address fields must be initialised before calling this.
 */
int
makemask(struct sockaddr_storage *ssp, int bitlen)
{
	u_char *p;
	int bits, i, len;

	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
		return (-1);
	if (bitlen > len * CHAR_BIT)
		return (-1);

	for (i = 0; i < len; i++) {
		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
		*p++ = (1 << bits) - 1;
		bitlen -= bits;
	}
	return 0;
}

After checking that the address family is known by calling sa_rawaddr() routine, as well as that the ‘bitlen’ argument (representing the prefix length) does not exceed the raw address’ length in bits, it enters a ‘for’ loop. This will update the raw address part of the ‘sockaddr’ structure masking it with ‘bits’. However, the current bitwise operations fail to correctly calculate the values when the ‘bitlen’ is not a multiple of 8.
To fix this, the calculation was re-written as shown below.

 		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
-		*p++ = (1 << bits) - 1;
+		*p++ = (u_char)~0 << (CHAR_BIT - bits);
 		bitlen -= bits;

In order to be able to handle such prefix lengths correctly.

Written by xorl

April 26, 2011 at 19:56

Posted in bugs, freebsd

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s