Discussion:
IP6 bugs in libspf2
Michael Deutschmann
2013-09-07 00:30:09 UTC
Permalink
Some serious bugs in libspf2's support for "ip6" mechanisms have come to
my attention. It was a little tricky to diagnose because the three bugs
interact in ways that make the problems hard to see.

It's relevant to discuss this here rather than some libspf2-specific
forum, because it means that SPF publishers may want to avoid using the
ip6 mechanism to avoid spurious fails.

The bugs are in the following function from "src/libspf2/spf_compile.c",
below:

static SPF_errcode_t
SPF_c_parse_ip6([...])
{
[...]
char buf[ INET_ADDRSTRLEN ];

[...
at this point start and end are char pointers
bracketing the IPv6 address, exclusive of any CIDR size
]

len = end - start;
if ( len > sizeof( buf ) - 1 )
return SPF_E_INVALID_IP6;

memcpy( buf, start, len );
buf[ len ] = '\0';
addr = SPF_mech_ip6_data(mech);
err = inet_pton( AF_INET6, buf, addr );
if ( err <= 0 )
return SPF_response_add_error_ptr(spf_response,
SPF_E_INVALID_IP6, NULL, buf, NULL);

return SPF_E_SUCCESS;
}

The errors are as follows:

1. "buf" is declared as an array of size INET_ADDRSTRLEN, which is
enough space to write any IPv4 address in ASCII (16 bytes including the
null terminator). That is way too small for an IPv6 address.
INET6_ADDRSTRLEN holds the correct value. Alone, this bug would cause
frequent permerrors whenever ip6 mechanisms are used.

2. In the case that the candidate address is too long (which will happen
often for valid addresses due to bug #1), the code does a "return
SPF_E_INVALID_IP6". But this doesn't actually cause an error to be
returned to the client -- it's the SPF_response_add_error_ptr() that
really does it. Instead of causing a permerror, these ip6 records are
silently ignored. Since bug #1 causes this to happen to valid ip6
mechanisms, this will cause spurious fails for IPv6 traffic.

3. Finally, the code assumes that inet_pton always actually supports
AF_INET6. I use uClibc, and if IPv6 is not explictly compiled into it,
it will provide an inet_pton() function that only works for
inet_pton(AF_INET,x,x), otherwise returning EAFNOSUPPORT. Alone this
bug would cause permerrors whenever a putative sender uses ip6 records,
but the other two bugs work together to make it invisible most of the
time.

I noticed the problem when a spam came in, putatively from gmail, and
had a permerror status. Using spfquery I determined that libspf2 was
barfing on the mechanism "ip6:2800:3f0:4000::/36". Some experimentation
determined that "ip6:2800:3f00:4000::/36" would have been accepted.

That falsely led me to think of a problem with inet_pton() being too
strict, so you can imagine my confusion when I found uClibc's inet_pton
never works and there was no sign of replacement code in libspf2.

What was really happening was that "2800:03f0:4000::" is 17 bytes and
thus seemed to pass due to the combined action of bugs #1 and #2.
"2800:3f0:4000::" is 16 bytes, so it avoided bug #1 and hit bug #3.

---- Michael Deutschmann <***@talamasca.ocis.net>
Scott Kitterman
2013-09-07 01:27:53 UTC
Permalink
Post by Michael Deutschmann
Some serious bugs in libspf2's support for "ip6" mechanisms have come to
my attention. It was a little tricky to diagnose because the three bugs
interact in ways that make the problems hard to see.
It's relevant to discuss this here rather than some libspf2-specific
forum, because it means that SPF publishers may want to avoid using the
ip6 mechanism to avoid spurious fails.
The bugs are in the following function from
"src/libspf2/spf_compile.c",
static SPF_errcode_t
SPF_c_parse_ip6([...])
{
[...]
char buf[ INET_ADDRSTRLEN ];
[...
at this point start and end are char pointers
bracketing the IPv6 address, exclusive of any CIDR size
]
len = end - start;
if ( len > sizeof( buf ) - 1 )
return SPF_E_INVALID_IP6;
memcpy( buf, start, len );
buf[ len ] = '\0';
addr = SPF_mech_ip6_data(mech);
err = inet_pton( AF_INET6, buf, addr );
if ( err <= 0 )
return SPF_response_add_error_ptr(spf_response,
SPF_E_INVALID_IP6, NULL, buf, NULL);
return SPF_E_SUCCESS;
}
1. "buf" is declared as an array of size INET_ADDRSTRLEN, which is
enough space to write any IPv4 address in ASCII (16 bytes including the
null terminator). That is way too small for an IPv6 address.
INET6_ADDRSTRLEN holds the correct value. Alone, this bug would cause
frequent permerrors whenever ip6 mechanisms are used.
Is the fix for this as simple as changing the "buf" size to INET6_ADDRSTRLEN?

That would at least reduce the frequency of the second bug, right?

Scott K
Michael Deutschmann
2013-09-07 07:44:13 UTC
Permalink
Post by Scott Kitterman
Is the fix for this as simple as changing the "buf" size to
INET6_ADDRSTRLEN?
Yes. These bugs are notable by their potential impact, not any
difficulty in fixing them.

---- Michael Deutschmann <***@talamasca.ocis.net>
alan
2013-09-07 16:44:31 UTC
Permalink
Post by Michael Deutschmann
Some serious bugs in libspf2's support for "ip6" mechanisms have come to
my attention. It was a little tricky to diagnose because the three bugs
interact in ways that make the problems hard to see.
It's relevant to discuss this here rather than some libspf2-specific
forum, because it means that SPF publishers may want to avoid using the
ip6 mechanism to avoid spurious fails.
i would say if it silent ignores ipv6: (that hit the bug)

then its the duty of the receivers (not the senders) to upgrade/fix

but as this will take time and senders want their mail unhampered

it would be better for 'us' (ie the community)
but someone better/smarter than me

to offer a way for spf-publishers to automate testing their spf records to find any ipv6: lines that will be ignored due to the bug so they can take action

(if none their spf needs no alteration)


if effected ranges found suggest a method for work around such as

(apologies in advance for the length of this post)

(example assuming original posters ip6:2800:3f0:4000::/36 is the only bug hitting range in his record )


instead of -all at end of record first fail normally for so far un passed ip4

-ip4:0.0.0.0/0

then fail the lowest largest possible ip6 supernet(s) below your affected ip6 subnet

-ip6:::/3 (0000:: - 1fff:ffff....)
-ip6:2000::/5 (2000:: - 27ff::ffff.....)
---
-ip6:2800::/18 (2800:: - 2800:1fff.....)
-ip6:2800:2000::/20 (2800:2000:: - 2800:2fff.....)
-ip6:2800:3000::/21 (2800:3000:: - 2800:37ff.....)
-ip6:2800:3800::/22 (2800:3800:: - 2800:3bff.....)
-ip6:2800:3c00::/23 (2800:3c00:: - 2800:3dff.....)
-ip6:2800:3e00::/24 (2800:3e00:: - 2800:3eff.....)

then fail the largest possible ip6 supernet(s) above your affected range(s)

-ip6:8000::/1 (8000:: - ffff:ffff...)
-ip6:4000::/2 (4000:: - 7fff:fff...)
-ip6:3000::/4 (3000:: - 3fff:fff....)
-ip6:2c00::/6 (2c00:: - 2fff:fff...)
-ip6:2a00::/7 (2a00:: - 2bff:fff...)
-ip6:2900::/8 (2900:: - 29ff:fff...)
----
-ip6:2800:3f80::/25 (2800:3f80:: - 2800:3fff.....)
-ip6:2800:3f40::/26 (2800:3f40:: - 2800:3f7f.....)
-ip6:2800:3f20::/27 (2800:3f20:: - 2800:3f3f.....)
-ip6:2800:3f10::/28 (2800:3f10:: - 2800:3f1f.....)
----
-ip6:2800:3f08::/29 (2800:3f08:: - 2800:3f0f.....)
-ip6:2800:3f04::/30 (2800:3f04:: - 2800:3f07:ff.....)
-ip6:2800:3f02::/31 (2800:3f02:: - 2800:3f03:ff.....)
-ip6:2800:3f01::/32 (2800:3f01:: - 2800:3f01:ff.....)

assuming all subnets below 32 dont trigger the bug
it at least cuts down the false-passes if you decide to ~all or ?all

(you can go on to the third/fourth etc but i got lazy and these are guaranteed to not hit the buffer issue)

the deeper you go the less chance forgeries will get false-passes from non-bug-effected receivers that continue to the redirect(below)

next use leftover dns lookups
v=spf1 a:_ipv6_1.domain.com a:_ipv6_2.domain.com etc up to 7

_ipv6_(1-9).domain.com IN A as many of your highest volume ipv6 ips (that are in an effected subnet) as possible

so some affected ip6-ips still pass positively

then (to ensure the normal records exp= text dosn't trigger for the below fails)
<http://www.openspf.org/RFC_4408#mod-exp>http://www.openspf.org/RFC_4408#mod-exp 6.2 end note

redirect:_ipv6fail-bugwarn.domain.com

_ipv6fail-bugwarn IN TXT "v=spf1 [-~?]all exp=_libspf2-may-be-broken.domain.com"

and
_libspf2-may-be-broken IN TXT "the libsp2 on receivers system may result in errors due to http://description-of-bug-page if this may be the case please report the case to them or submit it for review via http://reporter-tracker-url/?report=r=%{r}&s=%{s}&i=i{i}"

just as an idea
assuming sender interested in setting up a tracker
(exists: tracking would be nice but %{r} only available to exp=)

Loading...