2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 18:19:42 +00:00

Merge branch '4480-sig0-can-be-used-to-exhaust-cpu-resources-v6' into 'v9.20.0-release'

[CVE-2024-1975] Mitigate SIG(0) CPU resources exhaustion attack vectors

See merge request isc-private/bind9!689
This commit is contained in:
Nicki Křížek 2024-06-10 15:44:34 +00:00
commit b2cfdba565
24 changed files with 778 additions and 185 deletions

10
CHANGES
View File

@ -1,3 +1,13 @@
6402. [security] A malicious DNS client that sends many queries with a
SIG(0)-signed message can cause the server to respond
slowly or not respond at all to other clients. Use the
offload threadpool for SIG(0) signature verifications,
add the 'sig0checks-quota' configuration option to
introduce a quota for SIG(0)-signed queries running in
parallel and add the 'sig0checks-quota-exempt' option to
exempt certain clients by their IP/network addresses.
(CVE-2024-1975) [GL #4480]
6401. [security] An excessively large number of rrtypes per owner can
slow down database query processing, so a limit has been
placed on the number of rrtypes that can be stored per

View File

@ -2117,15 +2117,22 @@ cleanup:
static isc_result_t
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
isc_loop_t *loop, isc_job_cb cb, void *cbarg,
isc_result_t *sigresultp, isc_result_t *viewpatchresultp,
dns_view_t **viewp) {
UNUSED(srcaddr);
UNUSED(destaddr);
UNUSED(message);
UNUSED(env);
UNUSED(lsctx);
UNUSED(loop);
UNUSED(cb);
UNUSED(cbarg);
UNUSED(sigresultp);
*viewp = view;
*viewpatchresultp = ISC_R_SUCCESS;
return (ISC_R_SUCCESS);
}

View File

@ -109,6 +109,7 @@ options {\n\
# session-keyfile \"" NAMED_LOCALSTATEDIR "/run/named/session.key\";\n\
session-keyname local-ddns;\n\
startup-notify-rate 20;\n\
sig0checks-quota 1;\n\
statistics-file \"named.stats\";\n\
tcp-advertised-timeout 300;\n\
tcp-clients 150;\n\

View File

@ -278,6 +278,26 @@ struct zonelistentry {
ISC_LINK(struct zonelistentry) link;
};
/*%
* Message-to-view matching context to run message signature validation
* asynchronously.
*/
typedef struct matching_view_ctx {
isc_netaddr_t *srcaddr;
isc_netaddr_t *destaddr;
dns_message_t *message;
dns_aclenv_t *env;
ns_server_t *sctx;
isc_loop_t *loop;
isc_job_cb cb;
void *cbarg;
isc_result_t *sigresult;
isc_result_t *viewmatchresult;
isc_result_t quota_result;
dns_view_t **viewp;
dns_view_t *view;
} matching_view_ctx_t;
/*%
* Configuration context to retain for each view that allows
* new zones to be added at runtime.
@ -8412,6 +8432,8 @@ load_configuration(const char *filename, named_server_t *server,
configure_server_quota(maps, "recursive-clients",
&server->sctx->recursionquota);
configure_server_quota(maps, "update-quota", &server->sctx->updquota);
configure_server_quota(maps, "sig0checks-quota",
&server->sctx->sig0checksquota);
max = isc_quota_getmax(&server->sctx->recursionquota);
if (max > 1000) {
@ -8430,9 +8452,17 @@ load_configuration(const char *filename, named_server_t *server,
} else {
softquota = (max * 90) / 100;
}
isc_quota_soft(&server->sctx->recursionquota, softquota);
obj = NULL;
result = named_config_get(maps, "sig0checks-quota-exempt", &obj);
if (result == ISC_R_SUCCESS) {
result = cfg_acl_fromconfig(
obj, config, named_g_lctx, named_g_aclconfctx,
named_g_mctx, 0, &server->sctx->sig0checksquota_exempt);
INSIST(result == ISC_R_SUCCESS);
}
/*
* Set "blackhole". Only legal at options level; there is
* no default.
@ -10043,18 +10073,19 @@ shutdown_server(void *arg) {
isc_loopmgr_resume(named_g_loopmgr);
}
/*%
* Find a view that matches the source and destination addresses of a query.
*/
static isc_result_t
get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
dns_message_t *message, dns_aclenv_t *env,
isc_result_t *sigresult, dns_view_t **viewp) {
get_matching_view_sync(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
dns_message_t *message, dns_aclenv_t *env,
isc_result_t *sigresult, dns_view_t **viewp) {
dns_view_t *view;
REQUIRE(message != NULL);
REQUIRE(sigresult != NULL);
REQUIRE(viewp != NULL && *viewp == NULL);
/*
* We should not be running synchronous view matching if signature
* checking involves SIG(0). TSIG has priority of SIG(0), so if TSIG
* is set then we proceed anyway.
*/
INSIST(message->tsigkey != NULL || message->tsig != NULL ||
message->sig0 == NULL);
for (view = ISC_LIST_HEAD(named_g_server->viewlist); view != NULL;
view = ISC_LIST_NEXT(view, link))
@ -10064,12 +10095,10 @@ get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
{
const dns_name_t *tsig = NULL;
*sigresult = dns_message_rechecksig(message, view);
dns_message_resetsig(message);
*sigresult = dns_message_checksig(message, view);
if (*sigresult == ISC_R_SUCCESS) {
dns_tsigkey_t *tsigkey;
tsigkey = message->tsigkey;
tsig = dns_tsigkey_identity(tsigkey);
tsig = dns_tsigkey_identity(message->tsigkey);
}
if (dns_acl_allowed(srcaddr, tsig, view->matchclients,
@ -10088,6 +10117,191 @@ get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
return (ISC_R_NOTFOUND);
}
static void
get_matching_view_done(void *cbarg) {
matching_view_ctx_t *mvctx = cbarg;
dns_message_t *message = mvctx->message;
if (*mvctx->viewmatchresult == ISC_R_SUCCESS) {
INSIST(mvctx->view != NULL);
dns_view_attach(mvctx->view, mvctx->viewp);
}
mvctx->cb(mvctx->cbarg);
if (mvctx->quota_result == ISC_R_SUCCESS) {
isc_quota_release(&mvctx->sctx->sig0checksquota);
}
if (mvctx->view != NULL) {
dns_view_detach(&mvctx->view);
}
isc_loop_detach(&mvctx->loop);
ns_server_detach(&mvctx->sctx);
isc_mem_put(message->mctx, mvctx, sizeof(*mvctx));
dns_message_detach(&message);
}
static dns_view_t *
get_matching_view_next(dns_view_t *view, dns_rdataclass_t rdclass) {
if (view == NULL) {
view = ISC_LIST_HEAD(named_g_server->viewlist);
} else {
view = ISC_LIST_NEXT(view, link);
}
while (true) {
if (view == NULL || rdclass == view->rdclass ||
rdclass == dns_rdataclass_any)
{
return (view);
}
view = ISC_LIST_NEXT(view, link);
};
}
static void
get_matching_view_continue(void *cbarg, isc_result_t result) {
matching_view_ctx_t *mvctx = cbarg;
dns_view_t *view = NULL;
const dns_name_t *tsig = NULL;
*mvctx->sigresult = result;
if (result == ISC_R_SUCCESS) {
tsig = dns_tsigkey_identity(mvctx->message->tsigkey);
}
if (dns_acl_allowed(mvctx->srcaddr, tsig, mvctx->view->matchclients,
mvctx->env) &&
dns_acl_allowed(mvctx->destaddr, tsig,
mvctx->view->matchdestinations, mvctx->env) &&
!(mvctx->view->matchrecursiveonly &&
(mvctx->message->flags & DNS_MESSAGEFLAG_RD) == 0))
{
/*
* A matching view is found.
*/
*mvctx->viewmatchresult = ISC_R_SUCCESS;
get_matching_view_done(cbarg);
return;
}
dns_message_resetsig(mvctx->message);
view = get_matching_view_next(mvctx->view, mvctx->message->rdclass);
dns_view_detach(&mvctx->view);
if (view != NULL) {
/*
* Try the next view.
*/
dns_view_attach(view, &mvctx->view);
result = dns_message_checksig_async(
mvctx->message, view, mvctx->loop,
get_matching_view_continue, mvctx);
INSIST(result == DNS_R_WAIT);
return;
}
/*
* No matching view is found.
*/
*mvctx->viewmatchresult = ISC_R_NOTFOUND;
get_matching_view_done(cbarg);
}
/*%
* Find a view that matches the source and destination addresses of a query.
*/
static isc_result_t
get_matching_view(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
dns_message_t *message, dns_aclenv_t *env, ns_server_t *sctx,
isc_loop_t *loop, isc_job_cb cb, void *cbarg,
isc_result_t *sigresult, isc_result_t *viewmatchresult,
dns_view_t **viewp) {
dns_view_t *view = NULL;
isc_result_t result;
REQUIRE(message != NULL);
REQUIRE(sctx != NULL);
REQUIRE(loop == NULL || cb != NULL);
REQUIRE(sigresult != NULL);
REQUIRE(viewmatchresult != NULL);
REQUIRE(viewp != NULL && *viewp == NULL);
/* No offloading is requested if the loop is unset. */
if (loop == NULL) {
*viewmatchresult = get_matching_view_sync(
srcaddr, destaddr, message, env, sigresult, viewp);
return (*viewmatchresult);
}
/* Also no offloading when there is no view at all to match against. */
view = get_matching_view_next(NULL, message->rdclass);
if (view == NULL) {
*viewmatchresult = ISC_R_NOTFOUND;
return (*viewmatchresult);
}
dns_message_resetsig(message);
matching_view_ctx_t *mvctx = isc_mem_get(message->mctx, sizeof(*mvctx));
*mvctx = (matching_view_ctx_t){
.srcaddr = srcaddr,
.destaddr = destaddr,
.env = env,
.cb = cb,
.cbarg = cbarg,
.sigresult = sigresult,
.viewmatchresult = viewmatchresult,
.quota_result = ISC_R_UNSET,
.viewp = viewp,
};
ns_server_attach(sctx, &mvctx->sctx);
isc_loop_attach(loop, &mvctx->loop);
dns_message_attach(message, &mvctx->message);
/*
* If the message has a SIG0 signature which we are going to
* check, and the client is not exempt from the SIG(0) quota,
* then acquire a quota. TSIG has priority over SIG(0), so if
* TSIG is set then we don't care.
*/
if (message->tsigkey == NULL && message->tsig == NULL &&
message->sig0 != NULL)
{
if (sctx->sig0checksquota_exempt != NULL) {
int exempt_match;
result = dns_acl_match(srcaddr, NULL,
sctx->sig0checksquota_exempt,
env, &exempt_match, NULL);
if (result == ISC_R_SUCCESS && exempt_match > 0) {
mvctx->quota_result = ISC_R_EXISTS;
}
}
if (mvctx->quota_result == ISC_R_UNSET) {
mvctx->quota_result =
isc_quota_acquire(&sctx->sig0checksquota);
}
if (mvctx->quota_result == ISC_R_SOFTQUOTA) {
isc_quota_release(&sctx->sig0checksquota);
}
if (mvctx->quota_result != ISC_R_SUCCESS &&
mvctx->quota_result != ISC_R_EXISTS)
{
*mvctx->viewmatchresult = ISC_R_QUOTA;
isc_async_run(loop, get_matching_view_done, mvctx);
return (DNS_R_WAIT);
}
}
dns_view_attach(view, &mvctx->view);
result = dns_message_checksig_async(message, view, loop,
get_matching_view_continue, mvctx);
INSIST(result == DNS_R_WAIT);
return (DNS_R_WAIT);
}
void
named_server_create(isc_mem_t *mctx, named_server_t **serverp) {
isc_result_t result;

View File

@ -0,0 +1,16 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
options {
sig0checks-quota-exempt { unknownacl; };
};

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
acl goodacl {
192.168.0.1;
};
options {
sig0checks-quota-exempt { 10.0.0.0/8; 2001:db8::100; goodacl; };
};

View File

@ -29,6 +29,7 @@ rm -f */ans.run
rm -f Ksig0.example2.*
rm -f keyname keyname.err
rm -f ns1/example2.db
rm -f ns1/example2-toomanykeys.db
rm -f ns*/managed-keys.bind*
rm -f nsupdate.out.*
rm -f ns*/named.run.prev

View File

@ -44,6 +44,12 @@ zone "example2" {
allow-update { key sig0.example2.; };
};
zone "example2-toomanykeys" {
type primary;
file "example2-toomanykeys.db";
allow-update { key sig0.example2-toomanykeys.; };
};
zone "example3" {
type primary;
file "example3.db";

View File

@ -56,6 +56,13 @@ zone "example2" {
primaries { 10.53.0.1; };
};
zone "example2-toomanykeys" {
type secondary;
file "example2-toomanykeys.bk";
allow-update-forwarding { 10.53.0.1; };
primaries { 10.53.0.1; };
};
zone "example3" {
type secondary;
file "example3.bk";

View File

@ -32,7 +32,7 @@ else
fi
#
# SIG(0) required cryptographic support which may not be configured.
# SIG(0) requires cryptographic support which may not be configured.
#
keyname=$($KEYGEN -q -n HOST -a ${DEFAULT_ALGORITHM} -T KEY sig0.example2 2>keyname.err)
if test -n "$keyname"; then
@ -42,3 +42,12 @@ else
cat ns1/example1.db >ns1/example2.db
fi
cat_i <keyname.err
cat ns1/example1.db >ns1/example2-toomanykeys.db
for i in 1 2 3; do
keyname=$($KEYGEN -q -n HOST -a ${DEFAULT_ALGORITHM} -T KEY sig0.example2-toomanykeys 2>/dev/null)
if test -n "$keyname"; then
cat $keyname.key >>ns1/example2-toomanykeys.db
echo $keyname >keyname$i
fi
done

View File

@ -389,7 +389,7 @@ if test -f keyname; then
nextpart_thrice
ret=0
keyname=$(cat keyname)
$NSUPDATE -k $keyname.private -- - <<EOF || ret=1
$NSUPDATE -k $keyname.private -- - <<EOF >nsupdate.out.test$n 2>&1 || ret=1
local 10.53.0.1
server 10.53.0.3 ${PORT}
zone example2
@ -424,7 +424,7 @@ EOF
nextpart_thrice
ret=0
keyname=$(cat keyname)
$NSUPDATE -k $keyname.private -S -O -- - <<EOF || ret=1
$NSUPDATE -k $keyname.private -S -O -- - <<EOF >nsupdate.out.test$n 2>&1 || ret=1
local 10.53.0.1
server 10.53.0.3 ${TLSPORT}
zone example2
@ -454,6 +454,28 @@ EOF
status=$((status + ret))
n=$((n + 1))
fi
echo_i "checking update forwarding with sig0 with too many keys ($n)"
nextpart_thrice
ret=0
good=0
bad=0
for i in 1 2 3; do
keyname=$(cat keyname$i)
$NSUPDATE -d -D -k $keyname.private -- - <<EOF >nsupdate.out.test$n.$i 2>&1 && good=$((good + 1)) || bad=$((bad + 1))
local 10.53.0.1
server 10.53.0.3 ${PORT}
zone example2-toomanykeys
update add toomanykeys$i.example2-toomanykeys. 600 A 10.10.10.1
send
EOF
done
# There are three keys in the zone but named checks the signature using
# maximum two keys, so one of these updates should have been failed.
[ $good = 2 ] && [ $bad = 1 ] || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
fi
echo_i "attempting an update that should be rejected by ACL ($n)"

View File

@ -4000,6 +4000,32 @@ system.
the server will accept for updating local authoritiative zones or
forwarding to a primary server. The default is ``100``.
.. namedconf:statement:: sig0checks-quota
:tags: server
:short: Specifies the maximum number of concurrent SIG(0) signature checks that can be processed by the server.
This is the maximum number of simultaneous SIG(0)-signed messages that
the server accepts. If the quota is reached, then :iscman:`named` answers
with a status code of REFUSED. The value of ``0`` disables the quota. The
default is ``1``.
.. namedconf:statement:: sig0checks-quota-exempt
:tags: server
:short: Exempts specific clients or client groups from SIG(0) signature checking quota.
DNS clients can be exempted from SIG(0) signature checking quota with the
:any:`sig0checks-quota-exempt` clause using their IP and/or Network
addresses. The default value is an empty list.
Example:
::
sig0checks-quota-exempt {
10.0.0.0/8;
2001:db8::100;
};
.. _intervals:
Periodic Task Intervals

View File

@ -277,6 +277,8 @@ options {
sig-signing-signatures <integer>;
sig-signing-type <integer>;
sig-validity-interval <integer> [ <integer> ]; // obsolete
sig0checks-quota <integer>; // experimental
sig0checks-quota-exempt { <address_match_element>; ... }; // experimental
sortlist { <address_match_element>; ... }; // deprecated
stale-answer-client-timeout ( disabled | off | <integer> );
stale-answer-enable <boolean>;

View File

@ -34,6 +34,10 @@ Security Fixes
ISC would like to thank Toshifumi Sakaguchi who independently discovered
and responsibly reported the issue to ISC. :gl:`#4548`
- A malicious DNS client that sends many queries with a SIG(0)-signed message
can cause server to respond slowly or not respond at all for other clients.
:cve:`2024-1975` :gl:`#4480`
New Features
~~~~~~~~~~~~

View File

@ -352,6 +352,8 @@ struct dns_ednsopt {
unsigned char *value;
};
typedef void (*dns_message_cb_t)(void *arg, isc_result_t result);
/***
*** Functions
***/
@ -1328,24 +1330,23 @@ dns_message_checksig(dns_message_t *msg, dns_view_t *view);
*/
isc_result_t
dns_message_rechecksig(dns_message_t *msg, dns_view_t *view);
dns_message_checksig_async(dns_message_t *msg, dns_view_t *view,
isc_loop_t *loop, dns_message_cb_t cb, void *cbarg);
/*%<
* Reset the signature state and then if the message was signed,
* verify the message.
* Run dns_message_checksig() in an offloaded thread and return its result
* using the 'cb' callback function, running on the 'loop'.
*
* Requires:
*
*\li msg is a valid parsed message.
*\li view is a valid view or NULL
*\li view is a valid view or NULL.
*\li loop is a valid loop.
*\li cb is a valid callback function.
*
* Returns:
*
*\li #ISC_R_SUCCESS - the message was unsigned, or the message
* was signed correctly.
*\li #DNS_R_WAIT
*
*\li #DNS_R_EXPECTEDTSIG - A TSIG was expected, but not seen
*\li #DNS_R_UNEXPECTEDTSIG - A TSIG was seen but not expected
*\li #DNS_R_TSIGVERIFYFAILURE - The TSIG failed to verify
*/
void

View File

@ -29,6 +29,7 @@
#include <isc/string.h>
#include <isc/utf8.h>
#include <isc/util.h>
#include <isc/work.h>
#include <dns/dnssec.h>
#include <dns/keyvalues.h>
@ -180,6 +181,18 @@ msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
#define msgblock_get(block, type) \
((type *)msgblock_internalget(block, sizeof(type)))
/*
* A context type to pass information when checking a message signature
* asynchronously.
*/
typedef struct checksig_ctx {
dns_message_t *msg;
dns_view_t *view;
dns_message_cb_t cb;
void *cbarg;
isc_result_t result;
} checksig_ctx_t;
/*
* This function differs from public dns_message_puttemprdataset() that it
* requires the *rdatasetp to be associated, and it will disassociate and
@ -3177,12 +3190,6 @@ dns_message_resetsig(dns_message_t *msg) {
}
}
isc_result_t
dns_message_rechecksig(dns_message_t *msg, dns_view_t *view) {
dns_message_resetsig(msg);
return (dns_message_checksig(msg, view));
}
#ifdef SKAN_MSG_DEBUG
void
dns_message_dumpsig(dns_message_t *msg, char *txt1) {
@ -3211,6 +3218,47 @@ dns_message_dumpsig(dns_message_t *msg, char *txt1) {
}
#endif /* ifdef SKAN_MSG_DEBUG */
static void
checksig_run(void *arg) {
checksig_ctx_t *chsigctx = arg;
chsigctx->result = dns_message_checksig(chsigctx->msg, chsigctx->view);
}
static void
checksig_cb(void *arg) {
checksig_ctx_t *chsigctx = arg;
dns_message_t *msg = chsigctx->msg;
chsigctx->cb(chsigctx->cbarg, chsigctx->result);
dns_view_detach(&chsigctx->view);
isc_mem_put(msg->mctx, chsigctx, sizeof(*chsigctx));
dns_message_detach(&msg);
}
isc_result_t
dns_message_checksig_async(dns_message_t *msg, dns_view_t *view,
isc_loop_t *loop, dns_message_cb_t cb, void *cbarg) {
REQUIRE(DNS_MESSAGE_VALID(msg));
REQUIRE(view != NULL);
REQUIRE(loop != NULL);
REQUIRE(cb != NULL);
checksig_ctx_t *chsigctx = isc_mem_get(msg->mctx, sizeof(*chsigctx));
*chsigctx = (checksig_ctx_t){
.cb = cb,
.cbarg = cbarg,
.result = ISC_R_UNSET,
};
dns_message_attach(msg, &chsigctx->msg);
dns_view_attach(view, &chsigctx->view);
isc_work_enqueue(loop, checksig_run, checksig_cb, chsigctx);
return (DNS_R_WAIT);
}
isc_result_t
dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
isc_buffer_t b, msgb;
@ -3238,6 +3286,12 @@ dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
dns_rdata_sig_t sig;
dns_rdataset_t keyset;
isc_result_t result;
/*
* In order to protect from a possible DoS attack, we are
* going to check at most two KEY RRs.
*/
const size_t max_keys = 2;
size_t n;
result = dns_rdataset_first(msg->sig0);
INSIST(result == ISC_R_SUCCESS);
@ -3269,18 +3323,17 @@ dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
0, false, &keyset, NULL);
if (result != ISC_R_SUCCESS) {
/* XXXBEW Should possibly create a fetch here */
result = DNS_R_KEYUNAUTHORIZED;
goto freesig;
} else if (keyset.trust < dns_trust_secure) {
/* XXXBEW Should call a validator here */
} else if (keyset.trust < dns_trust_ultimate) {
result = DNS_R_KEYUNAUTHORIZED;
goto freesig;
}
result = dns_rdataset_first(&keyset);
INSIST(result == ISC_R_SUCCESS);
for (; result == ISC_R_SUCCESS;
result = dns_rdataset_next(&keyset))
for (n = 0; result == ISC_R_SUCCESS && n < max_keys;
n++, result = dns_rdataset_next(&keyset))
{
dst_key_t *key = NULL;
@ -3308,7 +3361,7 @@ dns_message_checksig(dns_message_t *msg, dns_view_t *view) {
break;
}
}
if (result == ISC_R_NOMORE) {
if (result == ISC_R_NOMORE || n == max_keys) {
result = DNS_R_KEYUNAUTHORIZED;
}

View File

@ -645,6 +645,8 @@ resquery_send(resquery_t *query);
static void
resquery_response(isc_result_t eresult, isc_region_t *region, void *arg);
static void
resquery_response_continue(void *arg, isc_result_t result);
static void
resquery_connected(isc_result_t eresult, isc_region_t *region, void *arg);
static void
fctx_try(fetchctx_t *fctx, bool retrying, bool badcache);
@ -777,6 +779,7 @@ release_fctx(fetchctx_t *fctx);
typedef struct respctx {
resquery_t *query;
fetchctx_t *fctx;
isc_mem_t *mctx;
isc_result_t result;
isc_buffer_t buffer;
unsigned int retryopts; /* updated options to pass to
@ -7233,7 +7236,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
isc_result_t result;
resquery_t *query = (resquery_t *)arg;
fetchctx_t *fctx = NULL;
respctx_t rctx;
respctx_t *rctx = NULL;
if (eresult == ISC_R_CANCELED) {
return;
@ -7252,21 +7255,22 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
inc_stats(fctx->res, dns_resstatscounter_responsev6);
}
rctx_respinit(query, fctx, eresult, region, &rctx);
rctx = isc_mem_get(fctx->mctx, sizeof(*rctx));
rctx_respinit(query, fctx, eresult, region, rctx);
if (eresult == ISC_R_SHUTTINGDOWN ||
atomic_load_acquire(&fctx->res->exiting))
{
result = ISC_R_SHUTTINGDOWN;
FCTXTRACE("resolver shutting down");
rctx.finish = NULL;
rctx_done(&rctx, result);
return;
rctx->finish = NULL;
rctx_done(rctx, result);
goto cleanup;
}
result = rctx_timedout(&rctx);
result = rctx_timedout(rctx);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
fctx->addrinfo = query->addrinfo;
@ -7276,9 +7280,9 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
/*
* Check whether the dispatcher has failed; if so we're done
*/
result = rctx_dispfail(&rctx);
result = rctx_dispfail(rctx);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
if (query->tsig != NULL) {
@ -7290,17 +7294,18 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
query->tsigkey);
if (result != ISC_R_SUCCESS) {
FCTXTRACE3("unable to set tsig key", result);
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
}
dns_message_setclass(query->rmessage, fctx->res->rdclass);
if ((rctx.retryopts & DNS_FETCHOPT_TCP) == 0) {
if ((rctx.retryopts & DNS_FETCHOPT_NOEDNS0) == 0) {
dns_adb_setudpsize(fctx->adb, query->addrinfo,
isc_buffer_usedlength(&rctx.buffer));
if ((rctx->retryopts & DNS_FETCHOPT_TCP) == 0) {
if ((rctx->retryopts & DNS_FETCHOPT_NOEDNS0) == 0) {
dns_adb_setudpsize(
fctx->adb, query->addrinfo,
isc_buffer_usedlength(&rctx->buffer));
} else {
dns_adb_plainresponse(fctx->adb, query->addrinfo);
}
@ -7309,38 +7314,39 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
/*
* Parse response message.
*/
result = rctx_parse(&rctx);
result = rctx_parse(rctx);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
/*
* Log the incoming packet.
*/
rctx_logpacket(&rctx);
rctx_logpacket(rctx);
if (query->rmessage->rdclass != fctx->res->rdclass) {
rctx.resend = true;
rctx->resend = true;
FCTXTRACE("bad class");
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
* Process receive opt record.
*/
rctx.opt = dns_message_getopt(query->rmessage);
if (rctx.opt != NULL) {
rctx_opt(&rctx);
rctx->opt = dns_message_getopt(query->rmessage);
if (rctx->opt != NULL) {
rctx_opt(rctx);
}
if (query->rmessage->cc_bad && (rctx.retryopts & DNS_FETCHOPT_TCP) == 0)
if (query->rmessage->cc_bad &&
(rctx->retryopts & DNS_FETCHOPT_TCP) == 0)
{
/*
* If the COOKIE is bad, assume it is an attack and
* keep listening for a good answer.
*/
rctx.nextitem = true;
rctx->nextitem = true;
if (isc_log_wouldlog(dns_lctx, ISC_LOG_INFO)) {
char addrbuf[ISC_SOCKADDR_FORMATSIZE];
isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf,
@ -7349,8 +7355,8 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
DNS_LOGMODULE_RESOLVER, ISC_LOG_INFO,
"bad cookie from %s", addrbuf);
}
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
@ -7377,27 +7383,55 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
result = same_question(fctx, query->rmessage);
if (result != ISC_R_SUCCESS) {
FCTXTRACE3("question section invalid", result);
rctx.nextitem = true;
rctx_done(&rctx, result);
return;
rctx->nextitem = true;
rctx_done(rctx, result);
goto cleanup;
}
break;
}
/*
* If the message is signed, check the signature. If not, this
* returns success anyway.
*/
result = dns_message_checksig(query->rmessage, fctx->res->view);
if (query->rmessage->tsigkey == NULL && query->rmessage->tsig == NULL &&
query->rmessage->sig0 != NULL)
{
/*
* If the message is not TSIG-signed (which has priorty) and is
* SIG(0)-signed (which consumes more resources), then run an
* asynchronous check.
*/
result = dns_message_checksig_async(
query->rmessage, fctx->res->view, fctx->loop,
resquery_response_continue, rctx);
INSIST(result == DNS_R_WAIT);
} else {
/*
* If the message is signed, check the signature. If not, this
* returns success anyway.
*/
result = dns_message_checksig(query->rmessage, fctx->res->view);
resquery_response_continue(rctx, result);
}
return;
cleanup:
isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx));
}
static void
resquery_response_continue(void *arg, isc_result_t result) {
respctx_t *rctx = arg;
fetchctx_t *fctx = rctx->fctx;
resquery_t *query = rctx->query;
if (result != ISC_R_SUCCESS) {
FCTXTRACE3("signature check failed", result);
if (result == DNS_R_UNEXPECTEDTSIG ||
result == DNS_R_EXPECTEDTSIG)
{
rctx.nextitem = true;
rctx->nextitem = true;
}
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
@ -7415,7 +7449,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
*/
if (dns_message_gettsig(query->rmessage, NULL) == NULL &&
!query->rmessage->cc_ok && !query->rmessage->cc_bad &&
(rctx.retryopts & DNS_FETCHOPT_TCP) == 0)
(rctx->retryopts & DNS_FETCHOPT_TCP) == 0)
{
if (dns_adb_getcookie(query->addrinfo, NULL, 0) >
CLIENT_COOKIE_SIZE)
@ -7431,10 +7465,10 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
"from %s",
addrbuf);
}
rctx.retryopts |= DNS_FETCHOPT_TCP;
rctx.resend = true;
rctx_done(&rctx, result);
return;
rctx->retryopts |= DNS_FETCHOPT_TCP;
rctx->resend = true;
rctx_done(rctx, result);
goto cleanup;
} else if (fctx->res->view->peers != NULL) {
dns_peer_t *peer = NULL;
isc_netaddr_t netaddr;
@ -7467,47 +7501,47 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
"from %s",
addrbuf);
}
rctx.retryopts |= DNS_FETCHOPT_TCP;
rctx.resend = true;
rctx_done(&rctx, result);
return;
rctx->retryopts |= DNS_FETCHOPT_TCP;
rctx->resend = true;
rctx_done(rctx, result);
goto cleanup;
}
}
}
}
rctx_edns(&rctx);
rctx_edns(rctx);
/*
* Deal with truncated responses by retrying using TCP.
*/
if ((query->rmessage->flags & DNS_MESSAGEFLAG_TC) != 0) {
rctx.truncated = true;
rctx->truncated = true;
}
if (rctx.truncated) {
if (rctx->truncated) {
inc_stats(fctx->res, dns_resstatscounter_truncated);
if ((rctx.retryopts & DNS_FETCHOPT_TCP) != 0) {
rctx.broken_server = DNS_R_TRUNCATEDTCP;
rctx.next_server = true;
if ((rctx->retryopts & DNS_FETCHOPT_TCP) != 0) {
rctx->broken_server = DNS_R_TRUNCATEDTCP;
rctx->next_server = true;
} else {
rctx.retryopts |= DNS_FETCHOPT_TCP;
rctx.resend = true;
rctx->retryopts |= DNS_FETCHOPT_TCP;
rctx->resend = true;
}
FCTXTRACE3("message truncated", result);
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
* Is it a query response?
*/
if (query->rmessage->opcode != dns_opcode_query) {
rctx.broken_server = DNS_R_UNEXPECTEDOPCODE;
rctx.next_server = true;
rctx->broken_server = DNS_R_UNEXPECTEDOPCODE;
rctx->next_server = true;
FCTXTRACE("invalid message opcode");
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
@ -7543,17 +7577,17 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
/*
* Bad server?
*/
result = rctx_badserver(&rctx, result);
result = rctx_badserver(rctx, result);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
/*
* Lame server?
*/
result = rctx_lameserver(&rctx);
result = rctx_lameserver(rctx);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
/*
@ -7578,9 +7612,9 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
query->rmessage->rcode == dns_rcode_yxdomain ||
query->rmessage->rcode == dns_rcode_nxdomain))
{
result = rctx_answer(&rctx);
result = rctx_answer(rctx);
if (result == ISC_R_COMPLETE) {
return;
goto cleanup;
}
} else if (query->rmessage->counts[DNS_SECTION_AUTHORITY] > 0 ||
query->rmessage->rcode == dns_rcode_noerror ||
@ -7590,7 +7624,7 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
* This might be an NXDOMAIN, NXRRSET, or referral.
* Call rctx_answer_none() to determine which it is.
*/
result = rctx_answer_none(&rctx);
result = rctx_answer_none(rctx);
switch (result) {
case ISC_R_SUCCESS:
case DNS_R_CHASEDSSERVERS:
@ -7609,28 +7643,28 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
* Something has gone wrong.
*/
if (result == DNS_R_FORMERR) {
rctx.next_server = true;
rctx->next_server = true;
}
FCTXTRACE3("rctx_answer_none", result);
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
} else {
/*
* The server is insane.
*/
/* XXXRTH Log */
rctx.broken_server = DNS_R_UNEXPECTEDRCODE;
rctx.next_server = true;
rctx->broken_server = DNS_R_UNEXPECTEDRCODE;
rctx->next_server = true;
FCTXTRACE("broken server: unexpected rcode");
rctx_done(&rctx, result);
return;
rctx_done(rctx, result);
goto cleanup;
}
/*
* Follow additional section data chains.
*/
rctx_additional(&rctx);
rctx_additional(rctx);
/*
* Cache the cacheable parts of the message. This may also
@ -7639,21 +7673,24 @@ resquery_response(isc_result_t eresult, isc_region_t *region, void *arg) {
if (WANTCACHE(fctx)) {
isc_result_t tresult;
tresult = cache_message(fctx, query->rmessage, query->addrinfo,
rctx.now);
rctx->now);
if (tresult != ISC_R_SUCCESS) {
FCTXTRACE3("cache_message complete", tresult);
rctx_done(&rctx, tresult);
return;
rctx_done(rctx, tresult);
goto cleanup;
}
}
/*
* Negative caching
*/
rctx_ncache(&rctx);
rctx_ncache(rctx);
FCTXTRACE("resquery_response done");
rctx_done(&rctx, result);
rctx_done(rctx, result);
cleanup:
isc_mem_putanddetach(&rctx->mctx, rctx, sizeof(*rctx));
}
/*
@ -7680,6 +7717,7 @@ rctx_respinit(resquery_t *query, fetchctx_t *fctx, isc_result_t result,
rctx->tnow = isc_time_now();
rctx->finish = &rctx->tnow;
rctx->now = (isc_stdtime_t)isc_time_seconds(&rctx->tnow);
isc_mem_attach(fctx->mctx, &rctx->mctx);
}
/*

View File

@ -2073,6 +2073,21 @@ check_options(const cfg_obj_t *options, const cfg_obj_t *config,
cfg_aclconfctx_create(mctx, &actx);
obj = NULL;
(void)cfg_map_get(options, "sig0checks-quota-exempt", &obj);
if (obj != NULL) {
dns_acl_t *acl = NULL;
tresult = cfg_acl_fromconfig(obj, config, logctx, actx, mctx, 0,
&acl);
if (acl != NULL) {
dns_acl_detach(&acl);
}
if (result == ISC_R_SUCCESS) {
result = tresult;
}
}
obj = NULL;
(void)cfg_map_get(options, "listen-on", &obj);
if (obj != NULL) {

View File

@ -1361,6 +1361,9 @@ static cfg_clausedef_t options_clauses[] = {
{ "session-keyalg", &cfg_type_astring, 0 },
{ "session-keyfile", &cfg_type_qstringornone, 0 },
{ "session-keyname", &cfg_type_astring, 0 },
{ "sig0checks-quota", &cfg_type_uint32, CFG_CLAUSEFLAG_EXPERIMENTAL },
{ "sig0checks-quota-exempt", &cfg_type_bracketed_aml,
CFG_CLAUSEFLAG_EXPERIMENTAL },
{ "sit-secret", NULL, CFG_CLAUSEFLAG_ANCIENT },
{ "stacksize", &cfg_type_size, CFG_CLAUSEFLAG_ANCIENT },
{ "startup-notify-rate", &cfg_type_uint32, 0 },

View File

@ -118,11 +118,27 @@
atomic_uint_fast64_t ns_client_requests = 0;
static atomic_uint_fast32_t last_sigchecks_quota_log = 0;
static bool
can_log_sigchecks_quota(void) {
isc_stdtime_t last;
isc_stdtime_t now = isc_stdtime_now();
last = atomic_exchange_relaxed(&last_sigchecks_quota_log, now);
if (now != last) {
return (true);
}
return (false);
}
static void
clientmgr_destroy_cb(void *arg);
static void
ns_client_dumpmessage(ns_client_t *client, const char *reason);
static void
ns_client_request_continue(void *arg);
static void
compute_cookie(ns_client_t *client, uint32_t when, const unsigned char *secret,
isc_buffer_t *buf);
@ -1667,6 +1683,16 @@ process_opt(ns_client_t *client, dns_rdataset_t *opt) {
return (result);
}
static void
ns_client_async_reset(ns_client_t *client) {
if (client->async) {
client->async = false;
if (client->handle != NULL) {
isc_nmhandle_unref(client->handle);
}
}
}
void
ns__client_reset_cb(void *client0) {
ns_client_t *client = client0;
@ -1693,6 +1719,8 @@ ns__client_reset_cb(void *client0) {
client->keytag_len = 0;
}
ns_client_async_reset(client);
client->state = NS_CLIENTSTATE_READY;
#ifdef WANT_SINGLETRACE
@ -1726,6 +1754,8 @@ ns__client_put_cb(void *client0) {
dns_message_puttemprdataset(client->message, &client->opt);
}
ns_client_async_reset(client);
dns_message_detach(&client->message);
/*
@ -1739,6 +1769,42 @@ ns__client_put_cb(void *client0) {
ns_clientmgr_detach(&manager);
}
static isc_result_t
ns_client_setup_view(ns_client_t *client, isc_netaddr_t *netaddr) {
isc_result_t result;
client->sigresult = client->viewmatchresult = ISC_R_UNSET;
if (client->async) {
isc_nmhandle_ref(client->handle);
}
result = client->manager->sctx->matchingview(
netaddr, &client->destaddr, client->message,
client->manager->aclenv, client->manager->sctx,
client->async ? client->manager->loop : NULL,
ns_client_request_continue, client, &client->sigresult,
&client->viewmatchresult, &client->view);
/* Async mode. */
if (result == DNS_R_WAIT) {
INSIST(client->async == true);
return (DNS_R_WAIT);
}
/*
* matchingview() returning anything other than DNS_R_WAIT means it's
* not running in async mode, in which case 'result' must be equal to
* 'client->viewmatchresult'.
*/
INSIST(result == client->viewmatchresult);
/* Non-async mode. */
ns_client_async_reset(client);
return (result);
}
/*
* Handle an incoming request event from the socket (UDP case)
* or tcpmsg (TCP case).
@ -1748,12 +1814,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
isc_region_t *region, void *arg) {
ns_client_t *client = NULL;
isc_result_t result;
isc_result_t sigresult = ISC_R_SUCCESS;
isc_buffer_t *buffer = NULL;
isc_buffer_t tbuffer;
dns_rdataset_t *opt = NULL;
const dns_name_t *signame = NULL;
bool ra; /* Recursion available. */
isc_netaddr_t netaddr;
int match;
dns_messageid_t id;
@ -1761,28 +1822,6 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
bool notimp;
size_t reqsize;
dns_aclenv_t *env = NULL;
#ifdef HAVE_DNSTAP
dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
#endif /* ifdef HAVE_DNSTAP */
static const char *ra_reasons[] = {
"ACLs not processed yet",
"no resolver in view",
"recursion not enabled for view",
"allow-recursion did not match",
"allow-query-cache did not match",
"allow-recursion-on did not match",
"allow-query-cache-on did not match",
};
enum refusal_reasons {
INVALID,
NO_RESOLVER,
RECURSION_DISABLED,
ALLOW_RECURSION,
ALLOW_QUERY_CACHE,
ALLOW_RECURSION_ON,
ALLOW_QUERY_CACHE_ON
} ra_refusal_reason = INVALID;
if (eresult != ISC_R_SUCCESS) {
return;
@ -1830,14 +1869,14 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
(void)atomic_fetch_add_relaxed(&ns_client_requests, 1);
isc_buffer_init(&tbuffer, region->base, region->length);
isc_buffer_add(&tbuffer, region->length);
buffer = &tbuffer;
isc_buffer_init(&client->tbuffer, region->base, region->length);
isc_buffer_add(&client->tbuffer, region->length);
client->buffer = &client->tbuffer;
client->peeraddr = isc_nmhandle_peeraddr(handle);
client->peeraddr_valid = true;
reqsize = isc_buffer_usedlength(buffer);
reqsize = isc_buffer_usedlength(client->buffer);
client->state = NS_CLIENTSTATE_WORKING;
@ -1876,7 +1915,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
ISC_LOG_DEBUG(3), "%s request",
TCP_CLIENT(client) ? "TCP" : "UDP");
result = dns_message_peekheader(buffer, &id, &flags);
result = dns_message_peekheader(client->buffer, &id, &flags);
if (result != ISC_R_SUCCESS) {
/*
* There isn't enough header to determine whether
@ -1951,7 +1990,7 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
/*
* It's a request. Parse it.
*/
result = dns_message_parse(client->message, buffer, 0);
result = dns_message_parse(client->message, client->buffer, 0);
if (result != ISC_R_SUCCESS) {
/*
* Parsing the request failed. Send a response
@ -2080,36 +2119,107 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
client->destsockaddr = isc_nmhandle_localaddr(handle);
isc_netaddr_fromsockaddr(&client->destaddr, &client->destsockaddr);
result = client->manager->sctx->matchingview(
&netaddr, &client->destaddr, client->message, env, &sigresult,
&client->view);
if (result != ISC_R_SUCCESS) {
char classname[DNS_RDATACLASS_FORMATSIZE];
/*
* Offload view matching only if we are going to check a SIG(0)
* signature.
*/
client->async = (client->message->tsigkey == NULL &&
client->message->tsig == NULL &&
client->message->sig0 != NULL);
result = ns_client_setup_view(client, &netaddr);
if (result == DNS_R_WAIT) {
return;
}
ns_client_request_continue(client);
}
static void
ns_client_request_continue(void *arg) {
ns_client_t *client = arg;
const dns_name_t *signame = NULL;
bool ra; /* Recursion available. */
isc_result_t result = ISC_R_UNSET;
static const char *ra_reasons[] = {
"ACLs not processed yet",
"no resolver in view",
"recursion not enabled for view",
"allow-recursion did not match",
"allow-query-cache did not match",
"allow-recursion-on did not match",
"allow-query-cache-on did not match",
};
enum refusal_reasons {
INVALID,
NO_RESOLVER,
RECURSION_DISABLED,
ALLOW_RECURSION,
ALLOW_QUERY_CACHE,
ALLOW_RECURSION_ON,
ALLOW_QUERY_CACHE_ON
} ra_refusal_reason = INVALID;
#ifdef HAVE_DNSTAP
dns_transport_type_t transport_type;
dns_dtmsgtype_t dtmsgtype;
#endif /* ifdef HAVE_DNSTAP */
INSIST(client->viewmatchresult != ISC_R_UNSET);
/*
* This function could be running asynchronously, in which case update
* the current 'now' for correct timekeeping.
*/
if (client->async) {
client->tnow = isc_time_now();
client->now = isc_time_seconds(&client->tnow);
}
if (client->viewmatchresult != ISC_R_SUCCESS) {
isc_buffer_t b;
isc_region_t *r;
/*
* Do a dummy TSIG verification attempt so that the
* response will have a TSIG if the query did, as
* required by RFC2845.
*/
isc_buffer_t b;
isc_region_t *r;
dns_message_resetsig(client->message);
r = dns_message_getrawmessage(client->message);
isc_buffer_init(&b, r->base, r->length);
isc_buffer_add(&b, r->length);
(void)dns_tsig_verify(&b, client->message, NULL, NULL);
dns_rdataclass_format(client->message->rdclass, classname,
sizeof(classname));
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"no matching view in class '%s'", classname);
ns_client_dumpmessage(client, "no matching view in class");
if (client->viewmatchresult == ISC_R_QUOTA) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(5),
"SIG(0) checks quota reached");
if (can_log_sigchecks_quota()) {
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_INFO,
"SIG(0) checks quota reached");
ns_client_dumpmessage(
client, "SIG(0) checks quota reached");
}
} else {
char classname[DNS_RDATACLASS_FORMATSIZE];
dns_rdataclass_format(client->message->rdclass,
classname, sizeof(classname));
ns_client_log(client, NS_LOGCATEGORY_CLIENT,
NS_LOGMODULE_CLIENT, ISC_LOG_DEBUG(1),
"no matching view in class '%s'",
classname);
ns_client_dumpmessage(client,
"no matching view in class");
}
ns_client_extendederror(client, DNS_EDE_PROHIBITED, NULL);
ns_client_error(client, notimp ? DNS_R_NOTIMP : DNS_R_REFUSED);
return;
ns_client_error(client, DNS_R_REFUSED);
goto cleanup;
}
if (isc_nm_is_proxy_handle(client->handle)) {
@ -2140,8 +2250,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
"ACL",
fmtbuf);
}
isc_nm_bad_request(handle);
return;
isc_nm_bad_request(client->handle);
goto cleanup;
}
/* allow by default */
@ -2161,8 +2271,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
"'allow-proxy-on' ACL",
fmtbuf);
}
isc_nm_bad_request(handle);
return;
isc_nm_bad_request(client->handle);
goto cleanup;
}
}
@ -2255,8 +2365,8 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
if (!(client->message->tsigstatus == dns_tsigerror_badkey &&
client->message->opcode == dns_opcode_update))
{
ns_client_error(client, sigresult);
return;
ns_client_error(client, client->sigresult);
goto cleanup;
}
}
@ -2311,6 +2421,9 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
if (client->udpsize > 512) {
dns_peer_t *peer = NULL;
uint16_t udpsize = client->view->maxudp;
isc_netaddr_t netaddr;
isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
(void)dns_peerlist_peerbyaddr(client->view->peers, &netaddr,
&peer);
if (peer != NULL) {
@ -2340,25 +2453,25 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
dns_dt_send(client->view, dtmsgtype, &client->peeraddr,
&client->destsockaddr, transport_type, NULL,
&client->requesttime, NULL, buffer);
&client->requesttime, NULL, client->buffer);
#endif /* HAVE_DNSTAP */
ns_query_start(client, handle);
ns_query_start(client, client->handle);
break;
case dns_opcode_update:
CTRACE("update");
#ifdef HAVE_DNSTAP
dns_dt_send(client->view, DNS_DTTYPE_UQ, &client->peeraddr,
&client->destsockaddr, transport_type, NULL,
&client->requesttime, NULL, buffer);
&client->requesttime, NULL, client->buffer);
#endif /* HAVE_DNSTAP */
ns_client_settimeout(client, 60);
ns_update_start(client, handle, sigresult);
ns_update_start(client, client->handle, client->sigresult);
break;
case dns_opcode_notify:
CTRACE("notify");
ns_client_settimeout(client, 60);
ns_notify_start(client, handle);
ns_notify_start(client, client->handle);
break;
case dns_opcode_iquery:
CTRACE("iquery");
@ -2368,6 +2481,9 @@ ns_client_request(isc_nmhandle_t *handle, isc_result_t eresult,
CTRACE("unknown opcode");
ns_client_error(client, DNS_R_NOTIMP);
}
cleanup:
ns_client_async_reset(client);
}
isc_result_t

View File

@ -167,6 +167,7 @@ struct ns_client {
ns_clientmgr_t *manager;
ns_clientstate_t state;
bool nodetach;
bool async;
unsigned int attributes;
dns_view_t *view;
dns_dispatch_t *dispatch;
@ -192,6 +193,10 @@ struct ns_client {
isc_time_t tnow;
dns_name_t signername; /*%< [T]SIG key name */
dns_name_t *signer; /*%< NULL if not valid sig */
isc_result_t sigresult;
isc_result_t viewmatchresult;
isc_buffer_t *buffer;
isc_buffer_t tbuffer;
isc_sockaddr_t peeraddr;
bool peeraddr_valid;

View File

@ -66,7 +66,9 @@ typedef void (*ns_fuzzcb_t)(void);
*/
typedef isc_result_t (*ns_matchview_t)(
isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr, dns_message_t *message,
dns_aclenv_t *env, isc_result_t *sigresultp, dns_view_t **viewp);
dns_aclenv_t *env, ns_server_t *sctx, isc_loop_t *loop, isc_job_cb cb,
void *cbarg, isc_result_t *sigresultp, isc_result_t *viewmatchresult,
dns_view_t **viewp);
/*%
* Server context.
@ -88,6 +90,8 @@ struct ns_server {
isc_quota_t tcpquota;
isc_quota_t xfroutquota;
isc_quota_t updquota;
isc_quota_t sig0checksquota;
dns_acl_t *sig0checksquota_exempt;
ISC_LIST(isc_quota_t) http_quotas;
isc_mutex_t http_quotas_lock;

View File

@ -66,6 +66,7 @@ ns_server_create(isc_mem_t *mctx, ns_matchview_t matchingview,
isc_quota_init(&sctx->tcpquota, 10);
isc_quota_init(&sctx->recursionquota, 100);
isc_quota_init(&sctx->updquota, 100);
isc_quota_init(&sctx->sig0checksquota, 1);
ISC_LIST_INIT(sctx->http_quotas);
isc_mutex_init(&sctx->http_quotas_lock);
@ -134,6 +135,11 @@ ns_server_detach(ns_server_t **sctxp) {
isc_mem_put(sctx->mctx, altsecret, sizeof(*altsecret));
}
if (sctx->sig0checksquota_exempt != NULL) {
dns_acl_detach(&sctx->sig0checksquota_exempt);
}
isc_quota_destroy(&sctx->sig0checksquota);
isc_quota_destroy(&sctx->updquota);
isc_quota_destroy(&sctx->recursionquota);
isc_quota_destroy(&sctx->tcpquota);

View File

@ -57,15 +57,22 @@ ns_server_t *sctx = NULL;
static isc_result_t
matchview(isc_netaddr_t *srcaddr, isc_netaddr_t *destaddr,
dns_message_t *message, dns_aclenv_t *env, isc_result_t *sigresultp,
dns_message_t *message, dns_aclenv_t *env, ns_server_t *lsctx,
isc_loop_t *loop, isc_job_cb cb, void *cbarg,
isc_result_t *sigresultp, isc_result_t *viewmatchresultp,
dns_view_t **viewp) {
UNUSED(srcaddr);
UNUSED(destaddr);
UNUSED(message);
UNUSED(env);
UNUSED(lsctx);
UNUSED(loop);
UNUSED(cb);
UNUSED(cbarg);
UNUSED(sigresultp);
UNUSED(viewp);
*viewmatchresultp = ISC_R_NOTIMPLEMENTED;
return (ISC_R_NOTIMPLEMENTED);
}