diff --git a/ext/filter/logical_filters.c b/ext/filter/logical_filters.c index 0ce2498f3a3a2..1bd9bad5afbe1 100644 --- a/ext/filter/logical_filters.c +++ b/ext/filter/logical_filters.c @@ -869,15 +869,120 @@ static int _php_filter_validate_ipv6(const char *str, size_t str_len, int ip[8]) } /* }}} */ -void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +/* From the tables in RFC 6890 - Special-Purpose IP Address Registriesi + * Including errata: https://www.rfc-editor.org/errata_search.php?rfc=6890&rec_status=1 */ +static bool ipv4_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private) +{ + *global = false; + *reserved = false; + *private = false; + + if (ip[0] == 0) { + /* RFC 0791 - This network */ + *reserved = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + /* RFC 1122 - This host on this network */ + *reserved = true; + } else if (ip[0] == 10) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127) { + /* RFC 6598 - Shared Address Space */ + } else if (ip[0] == 127) { + /* RFC 1122 - Loopback */ + *reserved = true; + } else if (ip[0] == 169 && ip[1] == 254) { + /* RFC 3927 - Link Local */ + *reserved = true; + } else if (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0) { + /* RFC 6890 - IETF Protocol Assignments */ + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 && ip[3] >= 0 && ip[3] <= 7) { + /* RFC 6333 - DS-Lite */ + } else if (ip[0] == 192 && ip[1] == 0 && ip[2] == 2) { + /* RFC 5737 - Documentation */ + } else if (ip[0] == 192 && ip[1] == 88 && ip[2] == 99) { + /* RFC 3068 - 6to4 Relay Anycast */ + *global = true; + } else if (ip[0] == 192 && ip[1] == 168) { + /* RFC 1918 - Private Use */ + *private = true; + } else if (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19) { + /* RFC 2544 - Benchmarking */ + } else if (ip[0] == 198 && ip[1] == 51 && ip[2] == 100) { + /* RFC 5737 - Documentation */ + } else if (ip[0] == 203 && ip[1] == 0 && ip[2] == 113) { + /* RFC 5737 - Documentation */ + } else if (ip[0] >= 240 && ip[1] <= 255) { + /* RFC 1122 - Reserved */ + *reserved = true; + } else if (ip[0] == 255 && ip[1] == 255 && ip[2] == 255 && ip[3] == 255) { + /* RFC 0919 - Limited Broadcast, Updated by RFC 8190, 2.2. */ + *reserved = true; + } else { + return false; + } + + return true; +} + +/* From the tables in RFC 6890 - Special-Purpose IP Address Registries */ +static bool ipv6_get_status_flags(const int ip[8], bool *global, bool *reserved, bool *private) { - /* validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a - * flag to throw out reserved ranges; multicast ranges... etc. If both - * allow_ipv4 and allow_ipv6 flags flag are used, then the first dot or - * colon determine the format */ + *global = false; + *reserved = false; + *private = false; + + if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0) { + /* RFC 4291 - Unspecified Address */ + *reserved = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 1) { + /* RFC 4291 - Loopback Address */ + *reserved = true; + } else if (ip[0] == 0x0064 && ip[1] == 0xff9b) { + /* RFC 6052 - IPv4-IPv6 Translation */ + *global = true; + } else if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) { + /* RFC 4291 - IPv4-mapped Address */ + *reserved = true; + } else if (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) { + /* RFC 6666 - Discard-Only Address Block */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0000) { + /* RFC 4380 - TEREDO */ + } else if (ip[0] == 0x2001 && ip[1] <= 0x01ff) { + /* RFC 2928 - IETF Protocol Assignments */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) { + /* RFC 5180 - Benchmarking */ + } else if (ip[0] == 0x2001 && ip[1] == 0x0db8) { + /* RFC 3849 - Documentation */ + } else if (ip[0] == 0x2001 && ip[1] >= 0x0010 && ip[1] <= 0x001f) { + /* RFC 4843 - ORCHID */ + } else if (ip[0] == 0x2002) { + /* RFC 3056 - 6to4 */ + } else if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) { + /* RFC 4193 - Unique-Local */ + *private = true; + } else if (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) { + /* RFC 4291 - Linked-Scoped Unicast */ + *reserved = true; + } else { + return false; + } - int ip[8]; - int mode; + return true; +} + +/* Validates an ipv4 or ipv6 IP, based on the flag (4, 6, or both) add a flag + * to throw out reserved ranges; multicast ranges... etc. If both allow_ipv4 + * and allow_ipv6 flags flag are used, then the first dot or colon determine + * the format */ +void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ +{ + int ip[8]; + int mode; + bool flag_global, flag_reserved, flag_private; /* flags for ranges as determined by RFC 6890 */ if (memchr(Z_STRVAL_P(value), ':', Z_STRLEN_P(value))) { mode = FORMAT_IPV6; @@ -895,86 +1000,35 @@ void php_filter_validate_ip(PHP_INPUT_FILTER_PARAM_DECL) /* {{{ */ RETURN_VALIDATION_FAILED } - switch (mode) { - case FORMAT_IPV4: - if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) { - RETURN_VALIDATION_FAILED - } + if (mode == FORMAT_IPV4) { + if (!_php_filter_validate_ipv4(Z_STRVAL_P(value), Z_STRLEN_P(value), ip)) { + RETURN_VALIDATION_FAILED + } - /* Check flags */ - if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 10) || - (ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31) || - (ip[0] == 192 && ip[1] == 168) - ) { - RETURN_VALIDATION_FAILED - } - } + if (!ipv4_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) { + return; /* no special block */ + } + } + else if (mode == FORMAT_IPV6) { + if (_php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip) < 1) { + RETURN_VALIDATION_FAILED + } - if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0) || - (ip[0] >= 240) || - (ip[0] == 127) || - (ip[0] == 169 && ip[1] == 254) - ) { - RETURN_VALIDATION_FAILED - } - } + if (!ipv6_get_status_flags(ip, &flag_global, &flag_reserved, &flag_private)) { + return; /* no special block */ + } + } - if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 100 && ip[1] >= 64 && ip[1] <= 127 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 0 ) || - (ip[0] == 192 && ip[1] == 0 && ip[2] == 2 ) || - (ip[0] == 198 && ip[1] >= 18 && ip[1] <= 19 ) || - (ip[0] == 198 && ip[1] == 51 && ip[2] == 100 ) || - (ip[0] == 203 && ip[1] == 0 && ip[2] == 113 ) - ) { - RETURN_VALIDATION_FAILED - } - } + if ((flags & FILTER_FLAG_GLOBAL_RANGE) && flag_global != true) { + RETURN_VALIDATION_FAILED + } - break; + if ((flags & FILTER_FLAG_NO_PRIV_RANGE) && flag_private == true) { + RETURN_VALIDATION_FAILED + } - case FORMAT_IPV6: - { - int res = 0; - res = _php_filter_validate_ipv6(Z_STRVAL_P(value), Z_STRLEN_P(value), ip); - if (res < 1) { - RETURN_VALIDATION_FAILED - } - /* Check flags */ - if (flags & FILTER_FLAG_NO_PRIV_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) { - RETURN_VALIDATION_FAILED - } - } - if (flags & FILTER_FLAG_NO_RES_RANGE || flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && (ip[7] == 0 || ip[7] == 1)) || - (ip[0] == 0x5f) || - (ip[0] >= 0xfe80 && ip[0] <= 0xfebf) || - (ip[0] == 0x2001 && (ip[1] == 0x0db8 || (ip[1] >= 0x0010 && ip[1] <= 0x001f))) || - (ip[0] == 0x3ff3) - ) { - RETURN_VALIDATION_FAILED - } - } - if (flags & FILTER_FLAG_GLOBAL_RANGE) { - if ( - (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && ip[4] == 0 && ip[5] == 0xffff) || - (ip[0] == 0x0100 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0) || - (ip[0] == 0x2001 && ip[1] <= 0x01ff) || - (ip[0] == 0x2001 && ip[1] == 0x0002 && ip[2] == 0) || - (ip[0] >= 0xfc00 && ip[0] <= 0xfdff) - ) { - RETURN_VALIDATION_FAILED - } - } - } - break; + if ((flags & FILTER_FLAG_NO_RES_RANGE) && flag_reserved == true) { + RETURN_VALIDATION_FAILED } } /* }}} */ diff --git a/ext/filter/tests/PMOPB45.phpt b/ext/filter/tests/CVE-2007-1900.phpt similarity index 72% rename from ext/filter/tests/PMOPB45.phpt rename to ext/filter/tests/CVE-2007-1900.phpt index 9aba547539718..d18fd4e5026f5 100644 --- a/ext/filter/tests/PMOPB45.phpt +++ b/ext/filter/tests/CVE-2007-1900.phpt @@ -1,5 +1,5 @@ --TEST-- -PMOPB-45-2007:PHP ext/filter Email Validation Vulnerability +CVE-2007-1900: PHP ext/filter Email Validation Vulnerability --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug42718-2.phpt b/ext/filter/tests/bug42718-2.phpt index 675ce1a747d89..ad8383d85db2e 100644 --- a/ext/filter/tests/bug42718-2.phpt +++ b/ext/filter/tests/bug42718-2.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #42718 - 2 (unsafe_raw filter not applied when configured as default filter) +Bug #42718 (unsafe_raw filter not applied when configured as default filter) --EXTENSIONS-- filter --INI-- diff --git a/ext/filter/tests/bug47435.phpt b/ext/filter/tests/bug47435.phpt index b035bc0a5f69f..64ee6f7fe66f7 100644 --- a/ext/filter/tests/bug47435.phpt +++ b/ext/filter/tests/bug47435.phpt @@ -18,10 +18,6 @@ var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("2001:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); var_dump(filter_var("240b:0010::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); -var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); -var_dump(filter_var("5f::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); -var_dump(filter_var("3ff3::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)); -var_dump(filter_var("3ff3::1", FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_RES_RANGE)); ?> --EXPECT-- string(7) "FC00::1" @@ -33,12 +29,8 @@ bool(false) string(11) "fe80:5:6::1" bool(false) string(12) "2001:0db8::1" -bool(false) +string(12) "2001:0db8::1" +string(12) "2001:0010::1" string(12) "2001:0010::1" -bool(false) string(12) "240b:0010::1" string(12) "240b:0010::1" -string(5) "5f::1" -bool(false) -string(7) "3ff3::1" -bool(false) diff --git a/ext/filter/tests/bug49274.phpt b/ext/filter/tests/bug49274.phpt index 12980db404177..3a2b434b0bc7d 100644 --- a/ext/filter/tests/bug49274.phpt +++ b/ext/filter/tests/bug49274.phpt @@ -1,5 +1,5 @@ --TEST-- -#49274, fatal error when an object does not implement toString +Bug #49274 (fatal error when an object does not implement toString) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug49510.phpt b/ext/filter/tests/bug49510.phpt index 2a45915561161..b34afffcc03b9 100644 --- a/ext/filter/tests/bug49510.phpt +++ b/ext/filter/tests/bug49510.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #49510 boolean validation fails with FILTER_NULL_ON_FAILURE +Bug #49510 (boolean validation fails with FILTER_NULL_ON_FAILURE) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug50632.phpt b/ext/filter/tests/bug50632.phpt index 5fa35c4d0bb69..07eae7aa54d35 100644 --- a/ext/filter/tests/bug50632.phpt +++ b/ext/filter/tests/bug50632.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 50632, filter_input() does not return default value if the variable does not exist +Bug #50632 (filter_input() does not return default value if the variable does not exist) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug51192.phpt b/ext/filter/tests/bug51192.phpt index 397f22d1d8817..fd4fd09c4422a 100644 --- a/ext/filter/tests/bug51192.phpt +++ b/ext/filter/tests/bug51192.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 51192, FILTER_VALIDATE_URL will invalidate a hostname that includes '-' +Bug #51192 (FILTER_VALIDATE_URL will invalidate a hostname that includes '-') --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug51368.phpt b/ext/filter/tests/bug51368.phpt index 73c743b5dee4f..a7bc26d478122 100644 --- a/ext/filter/tests/bug51368.phpt +++ b/ext/filter/tests/bug51368.phpt @@ -1,5 +1,5 @@ --TEST-- -FR #51368 (php_filter_float does not allow custom thousand separators) +Bug #51368 (php_filter_float does not allow custom thousand separators) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug64441.phpt b/ext/filter/tests/bug64441.phpt index 189f79d00c4ef..2dcc2a0f4fbcc 100644 --- a/ext/filter/tests/bug64441.phpt +++ b/ext/filter/tests/bug64441.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 64441, FILTER_VALIDATE_URL will invalidate a hostname that ended by dot +Bug #64441 (FILTER_VALIDATE_URL will invalidate a hostname that ended by dot) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug67167.01.phpt b/ext/filter/tests/bug67167.01.phpt index f51ccbe0dfc95..a55fabda304f1 100644 --- a/ext/filter/tests/bug67167.01.phpt +++ b/ext/filter/tests/bug67167.01.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #67167: object with VALIDATE_BOOLEAN and NULL_ON_FAILURE +Bug #67167 (object with VALIDATE_BOOLEAN and NULL_ON_FAILURE) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug67167.02.phpt b/ext/filter/tests/bug67167.02.phpt index 62927309e62a4..acbb7eeca5fe7 100644 --- a/ext/filter/tests/bug67167.02.phpt +++ b/ext/filter/tests/bug67167.02.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #67167: filter_var(null,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE) returns null +Bug #67167 (filter_var(null,FILTER_VALIDATE_BOOLEAN,FILTER_NULL_ON_FAILURE) returns null) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug7715.phpt b/ext/filter/tests/bug7715.phpt index 7c31b30d91c4b..9855a723fe987 100644 --- a/ext/filter/tests/bug7715.phpt +++ b/ext/filter/tests/bug7715.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 7715, floats value with integer or incomplete input +Bug #7715 (floats value with integer or incomplete input) --INI-- precision=14 --EXTENSIONS-- diff --git a/ext/filter/tests/bug7733.phpt b/ext/filter/tests/bug7733.phpt index a276d8a2b4a31..7a24decea2c54 100644 --- a/ext/filter/tests/bug7733.phpt +++ b/ext/filter/tests/bug7733.phpt @@ -1,5 +1,5 @@ --TEST-- -filter_var() Float exponential weird result +Bug #7733 (filter_var() Float exponential weird result) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug80584.phpt b/ext/filter/tests/bug80584.phpt index 49fa71a921f05..9446c649f4bad 100644 --- a/ext/filter/tests/bug80584.phpt +++ b/ext/filter/tests/bug80584.phpt @@ -1,5 +1,5 @@ --TEST-- -Bug #80584: "0x" and "0X" are considered valid hex numbers by filter_var() +Bug #80584 ("0x" and "0X" are considered valid hex numbers by filter_var()) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/bug8315.phpt b/ext/filter/tests/bug8315.phpt index e5334f43fc87f..55e1a745fa041 100644 --- a/ext/filter/tests/bug8315.phpt +++ b/ext/filter/tests/bug8315.phpt @@ -1,5 +1,5 @@ --TEST-- -bug 8315, NULL values halt the validation +Bug #8315 (NULL values halt the validation) --EXTENSIONS-- filter --FILE-- diff --git a/ext/filter/tests/gh16944.phpt b/ext/filter/tests/gh16944.phpt new file mode 100644 index 0000000000000..f6c492fed1abb --- /dev/null +++ b/ext/filter/tests/gh16944.phpt @@ -0,0 +1,10 @@ +--TEST-- +Bug GH-16944 (Invalid filtering of IPv6 with FILTER_FLAG_NO_RES_RANGE) +--EXTENSIONS-- +filter +--FILE-- + +--EXPECT-- +bool(false)