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:
@@ -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) {
|
||||
|
Reference in New Issue
Block a user