Browse Source

[core] support IPv6 in $HTTP["remote-ip"] CIDR cond match (fixes #2706)

x-ref:
  "Matching IPv6 addresses with $HTTP["remoteip"]"
  https://redmine.lighttpd.net/issues/2706

github: closes #52
personal/stbuehler/mod-csrf-old
Glenn Strauss 6 years ago
parent
commit
bed63b7962
  1. 12
      src/CMakeLists.txt
  2. 8
      src/Makefile.am
  3. 151
      src/configfile-glue.c
  4. 87
      src/test_configfile.c

12
src/CMakeLists.txt

@ -579,6 +579,16 @@ add_executable(test_base64
)
add_test(NAME test_base64 COMMAND test_base64)
add_executable(test_configfile
test_configfile.c
buffer.c
array.c
data_string.c
keyvalue.c
log.c
)
add_test(NAME test_configfile COMMAND test_configfile)
if(HAVE_PCRE_H)
target_link_libraries(lighttpd ${PCRE_LDFLAGS})
add_target_properties(lighttpd COMPILE_FLAGS ${PCRE_CFLAGS})
@ -703,6 +713,8 @@ if(WITH_LIBUNWIND)
add_target_properties(test_buffer COMPILE_FLAGS ${LIBUNWIND_CFLAGS})
target_link_libraries(test_base64 ${LIBUNWIND_LDFLAGS})
add_target_properties(test_base64 COMPILE_FLAGS ${LIBUNWIND_CFLAGS})
target_link_libraries(test_configfile ${PCRE_LDFLAGS} ${LIBUNWIND_LDFLAGS})
add_target_properties(test_configfile COMPILE_FLAGS ${PCRE_CFLAGS} ${LIBUNWIND_CFLAGS})
endif()
if(NOT WIN32)

8
src/Makefile.am

@ -1,12 +1,13 @@
AM_CFLAGS = $(FAM_CFLAGS) $(LIBUNWIND_CFLAGS)
noinst_PROGRAMS=lemon proc_open test_buffer test_base64
noinst_PROGRAMS=lemon proc_open test_buffer test_base64 test_configfile
sbin_PROGRAMS=lighttpd lighttpd-angel
LEMON=$(top_builddir)/src/lemon$(EXEEXT)
TESTS=\
test_buffer$(EXEEXT) \
test_base64$(EXEEXT)
test_base64$(EXEEXT) \
test_configfile$(EXEEXT)
lemon_SOURCES=lemon.c
@ -308,6 +309,9 @@ test_buffer_LDADD = $(LIBUNWIND_LIBS)
test_base64_SOURCES = test_base64.c base64.c buffer.c
test_base64_LDADD = $(LIBUNWIND_LIBS)
test_configfile_SOURCES = test_configfile.c buffer.c array.c data_string.c keyvalue.c log.c
test_configfile_LDADD = $(PCRE_LIB) $(LIBUNWIND_LIBS)
noinst_HEADERS = $(hdr)
EXTRA_DIST = \
mod_skeleton.c \

151
src/configfile-glue.c

@ -10,6 +10,9 @@
#include <string.h>
#include <stdlib.h>
#ifndef _WIN32
#include <arpa/inet.h>
#endif
/**
* like all glue code this file contains functions which
@ -237,6 +240,97 @@ static const char* cond_result_to_string(cond_result_t cond_result) {
}
}
static int config_addrstr_eq_remote_ip_mask(server *srv, const char *addrstr, int nm_bits, sock_addr *rmt) {
/* special-case 0 == nm_bits to mean "all bits of the address" in addrstr */
sock_addr val;
#ifdef HAVE_INET_PTON
if (1 == inet_pton(AF_INET, addrstr, &val.ipv4.sin_addr))
#else
if (INADDR_NONE != (val.ipv4.sin_addr = inet_addr(addrstr)))
#endif
{
/* build netmask */
uint32_t nm;
if (nm_bits > 32) {
log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv4 netmask too large:", nm_bits);
return -1;
}
nm = htonl(~((1u << (32 - (0 != nm_bits ? nm_bits : 32))) - 1));
if (rmt->plain.sa_family == AF_INET) {
return ((val.ipv4.sin_addr.s_addr & nm) == (rmt->ipv4.sin_addr.s_addr & nm));
#ifdef HAVE_IPV6
} else if (rmt->plain.sa_family == AF_INET6
&& IN6_IS_ADDR_V4MAPPED(&rmt->ipv6.sin6_addr)) {
in_addr_t x = *(in_addr_t *)(rmt->ipv6.sin6_addr.s6_addr+12);
return ((val.ipv4.sin_addr.s_addr & nm) == (x & nm));
#endif
} else {
return 0;
}
#if defined(HAVE_INET_PTON) && defined(HAVE_IPV6)
} else if (1 == inet_pton(AF_INET6, addrstr, &val.ipv6.sin6_addr)) {
if (nm_bits > 128) {
log_error_write(srv, __FILE__, __LINE__, "sd", "ERROR: ipv6 netmask too large:", nm_bits);
return -1;
}
if (rmt->plain.sa_family == AF_INET6) {
uint8_t *a = (uint8_t *)&val.ipv6.sin6_addr.s6_addr[0];
uint8_t *b = (uint8_t *)&rmt->ipv6.sin6_addr.s6_addr[0];
int match;
do {
match = (nm_bits >= 8)
? *a++ == *b++
: (*a >> (8 - nm_bits)) == (*b >> (8 - nm_bits));
} while (match && (nm_bits -= 8) > 0);
return match;
} else if (rmt->plain.sa_family == AF_INET
&& IN6_IS_ADDR_V4MAPPED(&val.ipv6.sin6_addr)) {
in_addr_t x = *(in_addr_t *)(val.ipv6.sin6_addr.s6_addr+12);
uint32_t nm =
htonl(~((1u << (32 - (0 != nm_bits ? (nm_bits > 96 ? nm_bits - 96 : 0) : 32))) - 1));
return ((x & nm) == (rmt->ipv4.sin_addr.s_addr & nm));
} else {
return 0;
}
#endif
} else {
log_error_write(srv, __FILE__, __LINE__, "ss", "ERROR: ip addr is invalid:", addrstr);
return -1;
}
}
static int config_addrbuf_eq_remote_ip_mask(server *srv, buffer *string, char *nm_slash, sock_addr *rmt) {
char *err;
int nm_bits = strtol(nm_slash + 1, &err, 10);
size_t addrstrlen = (size_t)(nm_slash - string->ptr);
char addrstr[64]; /*(larger than INET_ADDRSTRLEN and INET6_ADDRSTRLEN)*/
if (*err) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", string, err);
return -1;
}
if (nm_bits <= 0) {
if (*(nm_slash+1) == '\0') {
log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", string);
} else {
log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask <= 0:", string, err);
}
return -1;
}
if (addrstrlen >= sizeof(addrstr)) {
log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: address string too long:", string);
return -1;
}
memcpy(addrstr, string->ptr, addrstrlen);
addrstr[addrstrlen] = '\0';
return config_addrstr_eq_remote_ip_mask(srv, addrstr, nm_bits, rmt);
}
static cond_result_t config_check_cond_cached(server *srv, connection *con, data_config *dc);
static cond_result_t config_check_cond_nocache(server *srv, connection *con, data_config *dc) {
@ -370,61 +464,14 @@ static cond_result_t config_check_cond_nocache(server *srv, connection *con, dat
if ((dc->cond == CONFIG_COND_EQ ||
dc->cond == CONFIG_COND_NE) &&
(con->dst_addr.plain.sa_family == AF_INET) &&
(NULL != (nm_slash = strchr(dc->string->ptr, '/')))) {
int nm_bits;
long nm;
char *err;
struct in_addr val_inp;
if (*(nm_slash+1) == '\0') {
log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: no number after / ", dc->string);
return COND_RESULT_FALSE;
switch (config_addrbuf_eq_remote_ip_mask(srv, dc->string, nm_slash, &con->dst_addr)) {
case 1: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
case 0: return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
case -1: return COND_RESULT_FALSE; /*(error parsing configfile entry)*/
}
nm_bits = strtol(nm_slash + 1, &err, 10);
if (*err) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: non-digit found in netmask:", dc->string, err);
return COND_RESULT_FALSE;
}
if (nm_bits > 32 || nm_bits < 0) {
log_error_write(srv, __FILE__, __LINE__, "sbs", "ERROR: invalid netmask:", dc->string, err);
return COND_RESULT_FALSE;
}
/* take IP convert to the native */
buffer_copy_string_len(srv->cond_check_buf, dc->string->ptr, nm_slash - dc->string->ptr);
#ifdef __WIN32
if (INADDR_NONE == (val_inp.s_addr = inet_addr(srv->cond_check_buf->ptr))) {
log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);
return COND_RESULT_FALSE;
}
#else
if (0 == inet_aton(srv->cond_check_buf->ptr, &val_inp)) {
log_error_write(srv, __FILE__, __LINE__, "sb", "ERROR: ip addr is invalid:", srv->cond_check_buf);
return COND_RESULT_FALSE;
}
#endif
/* build netmask */
nm = nm_bits ? htonl(~((1 << (32 - nm_bits)) - 1)) : 0;
if ((val_inp.s_addr & nm) == (con->dst_addr.ipv4.sin_addr.s_addr & nm)) {
return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_TRUE : COND_RESULT_FALSE;
} else {
return (dc->cond == CONFIG_COND_EQ) ? COND_RESULT_FALSE : COND_RESULT_TRUE;
}
} else {
l = con->dst_addr_buf;
}
l = con->dst_addr_buf;
break;
}
case COMP_HTTP_SCHEME:

87
src/test_configfile.c

@ -0,0 +1,87 @@
#include "configfile-glue.c"
#include <stdlib.h>
#include <stdio.h>
const struct {
const char *string;
const char *rmtstr;
int rmtfamily;
int expect;
} rmtmask[] = {
{ "1.0.0.1/1", "1.0.0.1", AF_INET, 1 }
,{ "254.254.254.254/1", "254.0.0.1", AF_INET, 1 }
,{ "254.254.254.252/31", "254.254.254.253", AF_INET, 1 }
,{ "254.254.254.253/31", "254.254.254.254", AF_INET, 0 }
,{ "254.254.254.253/32", "254.254.254.254", AF_INET, 0 }
,{ "254.254.254.254/32", "254.254.254.254", AF_INET, 1 }
#ifdef HAVE_IPV6
,{ "2001::/3", "2001::1", AF_INET6, 1 }
,{ "2f01::/5", "2701::1", AF_INET6, 0 }
,{ "2f01::/32", "2f01::1", AF_INET6, 1 }
,{ "2f01::/32", "2f02::1", AF_INET6, 0 }
,{ "2001::1/127", "2001::1", AF_INET6, 1 }
,{ "2001::1/127", "2001::2", AF_INET6, 0 }
,{ "2001::2/128", "2001::2", AF_INET6, 1 }
,{ "2001::2/128", "2001::3", AF_INET6, 0 }
,{ "1.0.0.1/1", "::ffff:1.0.0.1", AF_INET6, 1 }
,{ "254.254.254.254/1", "::ffff:254.0.0.1", AF_INET6, 1 }
,{ "254.254.254.252/31", "::ffff:254.254.254.253", AF_INET6, 1 }
,{ "254.254.254.253/31", "::ffff:254.254.254.254", AF_INET6, 0 }
,{ "254.254.254.253/32", "::ffff:254.254.254.254", AF_INET6, 0 }
,{ "254.254.254.254/32", "::ffff:254.254.254.254", AF_INET6, 1 }
,{ "::ffff:1.0.0.1/97", "1.0.0.1", AF_INET, 1 }
,{ "::ffff:254.254.254.254/97", "254.0.0.1", AF_INET, 1 }
,{ "::ffff:254.254.254.252/127", "254.254.254.253", AF_INET, 1 }
,{ "::ffff:254.254.254.253/127", "254.254.254.254", AF_INET, 0 }
,{ "::ffff:254.254.254.253/128", "254.254.254.254", AF_INET, 0 }
,{ "::ffff:254.254.254.254/128", "254.254.254.254", AF_INET, 1 }
#endif
};
static void test_configfile_addrbuf_eq_remote_ip_mask (void) {
int i, m;
buffer * const s = buffer_init();
sock_addr rmt;
for (i = 0; i < (int)(sizeof(rmtmask)/sizeof(rmtmask[0])); ++i) {
#ifndef HAVE_INET_PTON
rmt.ipv4.sin_family = AF_INET;
rmt.ipv4.sin_addr.s_addr = inet_addr(rmtmask[i].rmtstr);
#else
if (rmtmask[i].rmtfamily == AF_INET) {
rmt.ipv4.sin_family = AF_INET;
inet_pton(AF_INET, rmtmask[i].rmtstr, &rmt.ipv4.sin_addr);
#ifdef HAVE_IPV6
} else if (rmtmask[i].rmtfamily == AF_INET6) {
rmt.ipv6.sin6_family = AF_INET6;
inet_pton(AF_INET6, rmtmask[i].rmtstr, &rmt.ipv6.sin6_addr);
#endif
} else {
continue;
}
#endif
buffer_copy_string(s, rmtmask[i].string);
m = config_addrbuf_eq_remote_ip_mask(NULL,s,strchr(s->ptr,'/'),&rmt);
if (m != rmtmask[i].expect) {
fprintf(stderr, "failed assertion: %s %s %s\n",
rmtmask[i].string,
rmtmask[i].expect ? "==" : "!=",
rmtmask[i].rmtstr);
exit(-1);
}
}
buffer_free(s);
}
int main (void) {
test_configfile_addrbuf_eq_remote_ip_mask();
return 0;
}
/*
* stub functions (for linking)
*/
void fd_close_on_exec(int fd) { UNUSED(fd); };
Loading…
Cancel
Save