2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 14:35:26 +00:00

2375. [security] Fully randomize UDP query ports to improve

forgery resilience. [RT #17949, #18098]
This commit is contained in:
Tatuya JINMEI 神明達哉
2008-06-23 19:41:20 +00:00
parent 40976ef802
commit 386d3a99c1
26 changed files with 3403 additions and 1553 deletions

View File

@@ -15,7 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: server.c,v 1.508 2008/05/21 23:47:00 tbox Exp $ */
/* $Id: server.c,v 1.509 2008/06/23 19:41:18 jinmei Exp $ */
/*! \file */
@@ -33,6 +33,7 @@
#include <isc/httpd.h>
#include <isc/lex.h>
#include <isc/parseint.h>
#include <isc/portset.h>
#include <isc/print.h>
#include <isc/resource.h>
#include <isc/stdio.h>
@@ -538,13 +539,15 @@ mustbesecure(const cfg_obj_t *mbs, dns_resolver_t *resolver)
*/
static isc_result_t
get_view_querysource_dispatch(const cfg_obj_t **maps,
int af, dns_dispatch_t **dispatchp)
int af, dns_dispatch_t **dispatchp,
isc_boolean_t is_firstview)
{
isc_result_t result;
dns_dispatch_t *disp;
isc_sockaddr_t sa;
unsigned int attrs, attrmask;
const cfg_obj_t *obj = NULL;
unsigned int maxdispatchbuffers;
/*
* Make compiler happy.
@@ -596,6 +599,20 @@ get_view_querysource_dispatch(const cfg_obj_t **maps,
attrs |= DNS_DISPATCHATTR_IPV6;
break;
}
if (isc_sockaddr_getport(&sa) == 0) {
attrs |= DNS_DISPATCHATTR_EXCLUSIVE;
maxdispatchbuffers = 4096;
} else {
INSIST(obj != NULL);
if (is_firstview) {
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_INFO,
"using specific query-source port "
"suppresses port randomization and can be "
"insecure.");
}
maxdispatchbuffers = 1000;
}
attrmask = 0;
attrmask |= DNS_DISPATCHATTR_UDP;
attrmask |= DNS_DISPATCHATTR_TCP;
@@ -605,7 +622,7 @@ get_view_querysource_dispatch(const cfg_obj_t **maps,
disp = NULL;
result = dns_dispatch_getudp(ns_g_dispatchmgr, ns_g_socketmgr,
ns_g_taskmgr, &sa, 4096,
1000, 32768, 16411, 16433,
maxdispatchbuffers, 32768, 16411, 16433,
attrs, attrmask, &disp);
if (result != ISC_R_SUCCESS) {
isc_sockaddr_t any;
@@ -1015,7 +1032,6 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
isc_boolean_t rfc1918;
isc_boolean_t empty_zones_enable;
const cfg_obj_t *disablelist = NULL;
isc_uint32_t nqports, qports_updateinterval;
dns_stats_t *resstats = NULL;
dns_stats_t *resquerystats = NULL;
@@ -1272,8 +1288,12 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
*
* XXXRTH Hardwired number of tasks.
*/
CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4));
CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6));
CHECK(get_view_querysource_dispatch(maps, AF_INET, &dispatch4,
ISC_TF(ISC_LIST_PREV(view, link)
== NULL)));
CHECK(get_view_querysource_dispatch(maps, AF_INET6, &dispatch6,
ISC_TF(ISC_LIST_PREV(view, link)
== NULL)));
if (dispatch4 == NULL && dispatch6 == NULL) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"unable to obtain neither an IPv4 nor"
@@ -1281,93 +1301,11 @@ configure_view(dns_view_t *view, const cfg_obj_t *config,
result = ISC_R_UNEXPECTED;
goto cleanup;
}
obj = NULL;
(void)ns_config_get(maps, "use-queryport-pool", &obj);
if (obj == NULL || cfg_obj_asboolean(obj)) {
isc_sockaddr_t sa;
isc_boolean_t logit4 = ISC_FALSE, logit6 = ISC_FALSE;
resopts |= (DNS_RESOLVER_USEDISPATCHPOOL4 |
DNS_RESOLVER_USEDISPATCHPOOL6);
/* Check consistency with query-source(-v6) */
if (dispatch4 == NULL)
resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL4;
else {
result = dns_dispatch_getlocaladdress(dispatch4, &sa);
INSIST(result == ISC_R_SUCCESS);
if (isc_sockaddr_getport(&sa) != 0) {
logit4 = ISC_TRUE;
resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL4;
}
}
if (dispatch6 == NULL)
resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL6;
else {
result = dns_dispatch_getlocaladdress(dispatch6, &sa);
INSIST(result == ISC_R_SUCCESS);
if (isc_sockaddr_getport(&sa) != 0) {
logit6 = ISC_TRUE;
resopts &= ~DNS_RESOLVER_USEDISPATCHPOOL6;
}
}
if (logit4 && obj != NULL)
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
"specific query-source port "
"cannot coexist with queryport-pool. "
"(Pool disabled)");
if (logit6 && obj != NULL)
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
"specific query-source-v6 port "
"cannot coexist with queryport-pool. "
"(Pool disabled)");
}
CHECK(dns_view_createresolver(view, ns_g_taskmgr, 31,
ns_g_socketmgr, ns_g_timermgr,
resopts, ns_g_dispatchmgr,
dispatch4, dispatch6));
/*
* Query-port pool parameters.
*/
obj = NULL;
nqports = 8;
result = ns_config_get(maps, "queryport-pool-ports", &obj);
if (result == ISC_R_SUCCESS) {
if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
DNS_RESOLVER_USEDISPATCHPOOL6)) == 0) {
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
"queryport-pool-ports is effective only "
"with 'use-queryport-pool yes' (ignored)");
} else
nqports = cfg_obj_asuint32(obj);
}
obj = NULL;
qports_updateinterval = 15;
result = ns_config_get(maps, "queryport-pool-updateinterval", &obj);
if (result == ISC_R_SUCCESS) {
if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
DNS_RESOLVER_USEDISPATCHPOOL6)) == 0) {
cfg_obj_log(obj, ns_g_lctx, ISC_LOG_ERROR,
"queryport-pool-updateinterval is "
"effective only with 'use-queryport-pool "
"yes' (ignored)");
} else
qports_updateinterval = cfg_obj_asuint32(obj);
}
if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
DNS_RESOLVER_USEDISPATCHPOOL6)) != 0) {
CHECK(dns_resolver_createdispatchpool(view->resolver,
nqports,
qports_updateinterval
* 60));
}
if (resstats == NULL) {
CHECK(dns_generalstats_create(mctx, &resstats,
dns_resstatscounter_max));
@@ -2674,8 +2612,6 @@ adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
view != NULL;
view = ISC_LIST_NEXT(view, link)) {
dns_dispatch_t *dispatch6;
isc_boolean_t use_portpool = ISC_FALSE;
unsigned int resopts;
dispatch6 = dns_resolver_dispatchv6(view->resolver);
if (dispatch6 == NULL)
@@ -2683,19 +2619,16 @@ adjust_interfaces(ns_server_t *server, isc_mem_t *mctx) {
result = dns_dispatch_getlocaladdress(dispatch6, &addr);
if (result != ISC_R_SUCCESS)
goto fail;
resopts = dns_resolver_getoptions(view->resolver);
if ((resopts & (DNS_RESOLVER_USEDISPATCHPOOL4 |
DNS_RESOLVER_USEDISPATCHPOOL6)) != 0) {
/*
* If the resolver uses a dynamic pool of query ports
* with a specific source address, some of the current
* and future ports may override an existing wildcard
* IPv6 port. So we need to allow wildcard match
* in this case.
*/
use_portpool = ISC_TRUE;
}
result = add_listenelt(mctx, list, &addr, use_portpool);
/*
* We always add non-wildcard address regardless of whether
* the port is 'any' (the fourth arg is TRUE): if the port is
* specific, we need to add it since it may conflict with a
* listening interface; if it's zero, we'll dynamically open
* query ports, and some of them may override an existing
* wildcard IPv6 port.
*/
result = add_listenelt(mctx, list, &addr, ISC_TRUE);
if (result != ISC_R_SUCCESS)
goto fail;
}
@@ -2884,24 +2817,41 @@ set_limits(const cfg_obj_t **maps) {
SETLIMIT("files", openfiles, "open files");
}
static isc_result_t
portlist_fromconf(dns_portlist_t *portlist, unsigned int family,
const cfg_obj_t *ports)
static void
portset_fromconf(isc_portset_t *portset, const cfg_obj_t *ports,
isc_boolean_t positive)
{
const cfg_listelt_t *element;
isc_result_t result = ISC_R_SUCCESS;
for (element = cfg_list_first(ports);
element != NULL;
element = cfg_list_next(element)) {
const cfg_obj_t *obj = cfg_listelt_value(element);
in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
result = dns_portlist_add(portlist, family, port);
if (result != ISC_R_SUCCESS)
break;
if (cfg_obj_isuint32(obj)) {
in_port_t port = (in_port_t)cfg_obj_asuint32(obj);
if (positive)
isc_portset_add(portset, port);
else
isc_portset_remove(portset, port);
} else {
const cfg_obj_t *obj_loport, *obj_hiport;
in_port_t loport, hiport;
obj_loport = cfg_tuple_get(obj, "loport");
loport = (in_port_t)cfg_obj_asuint32(obj_loport);
obj_hiport = cfg_tuple_get(obj, "hiport");
hiport = (in_port_t)cfg_obj_asuint32(obj_hiport);
if (positive)
isc_portset_addrange(portset, loport, hiport);
else {
isc_portset_removerange(portset, loport,
hiport);
}
}
}
return (result);
}
static isc_result_t
@@ -2940,7 +2890,7 @@ load_configuration(const char *filename, ns_server_t *server,
const cfg_obj_t *options;
const cfg_obj_t *views;
const cfg_obj_t *obj;
const cfg_obj_t *v4ports, *v6ports;
const cfg_obj_t *usev4ports, *avoidv4ports, *usev6ports, *avoidv6ports;
const cfg_obj_t *maps[3];
const cfg_obj_t *builtin_views;
const cfg_listelt_t *element;
@@ -2952,7 +2902,9 @@ load_configuration(const char *filename, ns_server_t *server,
isc_uint32_t interface_interval;
isc_uint32_t heartbeat_interval;
isc_uint32_t udpsize;
in_port_t listen_port;
in_port_t listen_port, udpport_low, udpport_high;
isc_portset_t *v4portset = NULL;
isc_portset_t *v6portset = NULL;
int i;
cfg_aclconfctx_init(&aclconfctx);
@@ -3069,24 +3021,64 @@ load_configuration(const char *filename, ns_server_t *server,
CHECKM(ns_statschannels_configure(ns_g_server, config, &aclconfctx),
"configuring statistics server(s)");
v4ports = NULL;
v6ports = NULL;
(void)ns_config_get(maps, "avoid-v4-udp-ports", &v4ports);
(void)ns_config_get(maps, "avoid-v6-udp-ports", &v6ports);
if (v4ports != NULL || v6ports != NULL) {
dns_portlist_t *portlist = NULL;
result = dns_portlist_create(ns_g_mctx, &portlist);
if (result == ISC_R_SUCCESS && v4ports != NULL)
result = portlist_fromconf(portlist, AF_INET, v4ports);
if (result == ISC_R_SUCCESS && v6ports != NULL)
portlist_fromconf(portlist, AF_INET6, v6ports);
if (result == ISC_R_SUCCESS)
dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, portlist);
if (portlist != NULL)
dns_portlist_detach(&portlist);
CHECK(result);
} else
dns_dispatchmgr_setblackportlist(ns_g_dispatchmgr, NULL);
/*
* Configure sets of UDP query source ports.
*/
CHECKM(isc_portset_create(ns_g_mctx, &v4portset),
"creating UDP port set");
CHECKM(isc_portset_create(ns_g_mctx, &v6portset),
"creating UDP port set");
usev4ports = NULL;
usev6ports = NULL;
avoidv4ports = NULL;
avoidv6ports = NULL;
(void)ns_config_get(maps, "use-v4-udp-ports", &usev4ports);
if (usev4ports != NULL)
portset_fromconf(v4portset, usev4ports, ISC_TRUE);
else {
CHECKM(isc_net_getudpportrange(AF_INET, &udpport_low,
&udpport_high),
"get the default UDP/IPv4 port range");
if (udpport_low == udpport_high)
isc_portset_add(v4portset, udpport_low);
else {
isc_portset_addrange(v4portset, udpport_low,
udpport_high);
}
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"using default UDP/IPv4 port range: [%d, %d]",
udpport_low, udpport_high);
}
(void)ns_config_get(maps, "avoid-v4-udp-ports", &avoidv4ports);
if (avoidv4ports != NULL)
portset_fromconf(v4portset, avoidv4ports, ISC_FALSE);
(void)ns_config_get(maps, "use-v6-udp-ports", &usev6ports);
if (usev6ports != NULL)
portset_fromconf(v6portset, usev6ports, ISC_TRUE);
else {
CHECKM(isc_net_getudpportrange(AF_INET6, &udpport_low,
&udpport_high),
"get the default UDP/IPv6 port range");
if (udpport_low == udpport_high)
isc_portset_add(v6portset, udpport_low);
else {
isc_portset_addrange(v6portset, udpport_low,
udpport_high);
}
isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_SERVER, ISC_LOG_INFO,
"using default UDP/IPv6 port range: [%d, %d]",
udpport_low, udpport_high);
}
(void)ns_config_get(maps, "avoid-v6-udp-ports", &avoidv6ports);
if (avoidv6ports != NULL)
portset_fromconf(v6portset, avoidv6ports, ISC_FALSE);
dns_dispatchmgr_setavailports(ns_g_dispatchmgr, v4portset, v6portset);
/*
* Set the EDNS UDP size when we don't match a view.
@@ -3569,6 +3561,12 @@ load_configuration(const char *filename, ns_server_t *server,
result = ISC_R_SUCCESS;
cleanup:
if (v4portset != NULL)
isc_portset_destroy(ns_g_mctx, &v4portset);
if (v6portset != NULL)
isc_portset_destroy(ns_g_mctx, &v6portset);
cfg_aclconfctx_destroy(&aclconfctx);
if (parser != NULL) {