Project

General

Profile

Actions

Bug #5702

closed

Bug in code manipulating IP subnets - could be pervasive?

Added by Stilez y over 8 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Normal
Assignee:
Category:
-
Target version:
-
Start date:
12/27/2015
Due date:
% Done:

0%

Estimated time:
Plus Target Version:
Release Notes:
Affected Version:
Affected Architecture:

Description

This bug was uncovered in resolving PR #2317
I don't know how pervasive it is or how to efficiently fix it.

BACKGROUND TO THE PR:

util.inc included many IP and network conversion functions. Many of these did no input sanitation or checking in the past. When IPv6 came along, many functions continued to call code that used 32-bit integer manipulation for IPv6 and on x64, which often fails. The code didn't usually detect these, and allowed bad data to be passed back as good data. For example, long2ip based functions often treated a return value of FALSE (signifying bad data) the same as zero (good data) as they didn't check for "===false" first; they just assumed the input was valid. Two reasons this was a problem:

1) conversion/sanitation functions shouldn't return values which make it impossible to distinguish good from invalid data, or which appears to be trustworthy but hasn't actually been checked;
2) past work in this area showed such issues had indeed occurred many times and caused code bugs in practice; it is unlikely all of these are found so far.

So the above PR updated three small IP conversion functions that converted (int)IPv4 to and from (string)IPv4.

EFFECT OF PR:

Chris (correctly) noticed that this broke range validation on the page services_dhcp.php and perhaps others, and asked that it not be resubmitted until fixed. He left it for me to look into as the author.

In fact what this commit revealed was the exact opposite of this initial conclusion. When I looked carefully, the patched code was doing precisely the right thing, that it was intended to do. It was trapping bad data which had previously gone un-noticed, and highlighted a bug in the calling code, which was leading to ip2long() being fed invalid data. So it was (correctly) returning "bad data" rather than allowing this and returning rubbish.

The specific bug which caused the issue Chris noticed, was this line in services_dhcp.php:

$subnet_end = ip2ulong(long2ip32(ip2long($ifcfgip) | (~gen_subnet_mask_long($ifcfgsn))));

Suppose we set a subnet of x.x.x.x/24. Then gen_subnet_mask_long($ifcfgsn) will return 0x000000FF. But the subnet end IP gets calculated using ~(netmask), i.e., ~ 0x000000FF.... which on x64 will be 0xFFFFFFFFFFFFFF00 NOT 0xFFFFFF00. That is why this tightened function began trapping and returning an error as Chris reported- the calling function wasn't in fact providing it with valid IPv4 IPs in the arguments to convert to/from dotted format, and this simply hadn't been detected before now.

The fix is to use the function gen_subnetv4_max() which already exists and works correctly, rather than ~$netmask but it's not clear how widely this problem exists or how to find the places it occurs efficiently. I'm not sure how I'd go about finding all incorrect IUPv4 calcs which might be sending bad data to these functions, if I were asked. So I'm posting it here to see what people think best.

One idea is to scan the codebase for ~$var which often means a subnet calc. Another might be to return the old value but log the error and ask testers to review and send in that log after a while to catch the "big ones". Ignoring it seems a bad idea since we know these calls are being mis-called and return rubbish on bad data rather than detecting it. What's best?

Actions #1

Updated by Stilez y over 8 years ago

SEARCH shows there are only a few direct calls:

https://github.com/pfsense/pfsense/search?utf8=%E2%9C%93&q=long2ip32+%7C+ip2long32
https://github.com/pfsense/pfsense/search?utf8=%E2%9C%93&q=ip2ulong

If I check and fix them, will that suffice, and if not, what else?

Actions #2

Updated by Phillip Davis over 8 years ago

Yeh, I fixed something like that in (I think) services_dhcp_edit by masking it against 0xFFFFFFFF to make sure to only get the bottom 32 bits of whatever.

IMHO it would be good if no "top-level" code called tricky bit-manipulating routines like that sort of sequence you quote above - ip2ulong(long2ip32(ip2long()... - but just called the various "nice" routines that do gen_subnet() gen_subnet_mask() and the like. Then the top-level code can just get answers back that are reliable numbers and gen_subnet()... can check that they are being passed strings that look like a valid IP address representation. That would reduce the callers of ip2ulong()... stuff to higher-level routines that do validation on the way through.

Proper validation inside ip2ulong()... won't hurt either!

I guess this is where OO stuff can help, by hiding tricky internal routines and just letting callers see methods and objects that fit together. In whatever language, that can all be (re)designed nicely in V3.

Actions #3

Updated by Stilez y over 8 years ago

Actions #4

Updated by Stilez y over 8 years ago

I've split the above PR into 9 separate PRs to avoid merge conflicts.

It turns out that every call to ip2long32(), long2ip32() and ip2ulong() outside util.inc, is actually replicating existing high level IPv4 functions such as ip_greater_than(), is_inrange_v4() and ip_after() - and often doing it wrongly due to lack of x64 masking. So Ive replaced these by calls to the core IP manipulation functions.

Actions #5

Updated by Jim Thompson about 8 years ago

  • Assignee set to Anonymous

tread carefully, Steve.

Actions #6

Updated by Anonymous about 8 years ago

OP proposed PR which after discussion with the DEV team was superseded by PR 2414 and merged

Actions #7

Updated by Anonymous about 8 years ago

  • Status changed from New to Feedback
  • Assignee changed from Anonymous to Jim Thompson
Actions #8

Updated by Stilez y about 8 years ago

Taking a tip from Phil Davis, I've looked at the high level comparisons used in the codebase, and written a single high-level ip comparison function able to efficiently handle all comparisons likely to be needed between ip-to-ip, ip-to-range/subnet, and range/subnet-to-range/subnet, including those we have and those that are missing. Thanks for the idea!
( https://github.com/pfsense/pfsense/pull/2511 )

Actions #9

Updated by Jim Pingle over 4 years ago

  • Status changed from Feedback to Closed

PR was closed, so this shall follow.

Actions

Also available in: Atom PDF