diff --git a/doc/arm/changelog.rst b/doc/arm/changelog.rst index 9f0c194a97..9f201051d1 100644 --- a/doc/arm/changelog.rst +++ b/doc/arm/changelog.rst @@ -18,6 +18,8 @@ Changelog development. Regular users should refer to :ref:`Release Notes ` for changes relevant to them. +.. include:: ../changelog/changelog-9.21.14.rst +.. include:: ../changelog/changelog-9.21.13.rst .. include:: ../changelog/changelog-9.21.12.rst .. include:: ../changelog/changelog-9.21.11.rst .. include:: ../changelog/changelog-9.21.10.rst diff --git a/doc/arm/notes.rst b/doc/arm/notes.rst index 9a5a74633c..97300c407d 100644 --- a/doc/arm/notes.rst +++ b/doc/arm/notes.rst @@ -47,6 +47,8 @@ The list of known issues affecting the latest version in the 9.21 branch can be found at https://gitlab.isc.org/isc-projects/bind9/-/wikis/Known-Issues-in-BIND-9.21 +.. include:: ../notes/notes-9.21.14.rst +.. include:: ../notes/notes-9.21.13.rst .. include:: ../notes/notes-9.21.12.rst .. include:: ../notes/notes-9.21.11.rst .. include:: ../notes/notes-9.21.10.rst diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 8f29cdcba7..7a054cb610 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2186,6 +2186,8 @@ Boolean Options autodetection of DNS COOKIE support to determine when to retry a request over TCP. + For DNAME lookups the default is ``yes`` and it is enforced. Servers + serving DNAME must correctly support DNS over TCP. .. note:: If a UDP response is signed using TSIG, :iscman:`named` accepts it even if diff --git a/doc/changelog/changelog-9.21.13.rst b/doc/changelog/changelog-9.21.13.rst new file mode 100644 index 0000000000..2a10446273 --- /dev/null +++ b/doc/changelog/changelog-9.21.13.rst @@ -0,0 +1,18 @@ +.. 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. + +BIND 9.21.13 +------------ + +.. note:: + + The BIND 9.21.13 release was withdrawn after the discovery of a + regression in a security fix in it during pre-release testing. diff --git a/doc/changelog/changelog-9.21.14.rst b/doc/changelog/changelog-9.21.14.rst new file mode 100644 index 0000000000..72b9bae0a5 --- /dev/null +++ b/doc/changelog/changelog-9.21.14.rst @@ -0,0 +1,441 @@ +.. 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. + +BIND 9.21.14 +------------ + +Security Fixes +~~~~~~~~~~~~~~ + +- [CVE-2025-8677] DNSSEC validation fails if matching but invalid DNSKEY + is found. ``1d851c23529`` + + Previously, if a matching but cryptographically invalid key was + encountered during DNSSEC validation, the key was skipped and not + counted towards validation failures. :iscman:`named` now treats such + DNSSEC keys as hard failures and the DNSSEC validation fails + immediately, instead of continuing with the next DNSKEYs in the RRset. + + ISC would like to thank Zuyao Xu and Xiang Li from the All-in-One + Security and Privacy Laboratory at Nankai University for bringing this + vulnerability to our attention. :gl:`#5343` + +- [CVE-2025-40778] Address various spoofing attacks. ``7b95c382dbd`` + + Previously, several issues could be exploited to poison a DNS cache + with spoofed records for zones which were not DNSSEC-signed or if the + resolver was configured to not do DNSSEC validation. These issues were + assigned CVE-2025-40778 and have now been fixed. + + As an additional layer of protection, :iscman:`named` no longer + accepts DNAME records or extraneous NS records in the AUTHORITY + section unless these are received via spoofing-resistant transport + (TCP, UDP with DNS cookies, TSIG, or SIG(0)). + + ISC would like to thank Yuxiao Wu, Yunyi Zhang, Baojun Liu, and Haixin + Duan from Tsinghua University for bringing this vulnerability to our + attention. :gl:`#5414` + +- [CVE-2025-40780] Cache-poisoning due to weak pseudo-random number + generator. ``6876753c7cc`` + + It was discovered during research for an upcoming academic paper that + a xoshiro128\*\* internal state can be recovered by an external 3rd + party, allowing the prediction of UDP ports and DNS IDs in outgoing + queries. This could lead to an attacker spoofing the DNS answers with + great efficiency and poisoning the DNS cache. + + The internal random generator has been changed to a cryptographically + secure pseudo-random generator. + + ISC would like to thank Prof. Amit Klein and Omer Ben Simhon from + Hebrew University of Jerusalem for bringing this vulnerability to our + attention. :gl:`#5484` + +New Features +~~~~~~~~~~~~ + +- Add extra tokens to the zone file name template. ``b449fa95005`` + + Extend the `$name`, `$view` and `$type` tokens (expanding into the + zone name, zone's view name and type); the new following tokens are + now also accepted: + + - `$name` or `%s` is replaced with the zone name in lower case; + - `$type` or `%t` is replaced with the zone type -- i.e., primary, + secondary, etc); + - `$view` or `%v` is replaced with the view name; + - `$char1` or `%1` is replaced with the first character of the zone + name; + - `$char2` or `%2` is replaced with the second character of the zone + name (or a dot if there is no second character); + - `$char3` or `%3` is replaced with the third character of the zone + name (or a dot if there is no third character); + - `$label1` or `%z` is replaced with the toplevel domain of the zone + (or a dot if it is the root zone); + - `$label2` or `%y` is replaced with the next label under the toplevel + domain (or a dot if there is no next label); + - `$label3` or `%x` is replaced with the next-next label under the + toplevel domain (or a dot if there is no next-next label). + + :gl:`#85` :gl:`!10779` + +- Add support for synthetic records. ``cefed841046`` + + Add a query plugin which, in "reverse" mode, enables the server to + build a synthesized response to a PTR query when the PTR record + requested is not found in the zone. The dynamically-built name is + constructed from a static prefix (passed as a plugin parameter), the + IP address (extracted from the query name) and a suffix (also passed + as a plugin parameter). An `allow-synth` address-match list can be + used to limit the network addresses for which the plugin may generate + responses. The plugin can also be used in "forward" mode, to + build synthesized A/AAAA records from names using the same format as + the dynamically-built PTR names. The same parameters are used: the + plugin will react and answer a query if the name matches the + configured prefix and origin, and encodes an IP address that is within + `allow-synth`. :gl:`#1586` :gl:`!10348` + +- Support for zone-specific plugins. ``65fa5693572`` + + Query plugins can now be configured at the `zone` level, as well as + globally or at the `view` level. A plugin's hooks are then called only + while that specific zone's database is being used to answer a query. + + This simplifies the implementation of plugins that are only needed for + specific namespaces for which the server is authoritative. It can also + enable quicker responses, since plugins will only be called when they + are needed. :gl:`#5356` :gl:`!10483` + +- Add dnssec-policy keys configuration check to named-checkconf. + ``23a79b42ea4`` + + A new option `-k` is added to `named-checkconf` that allows checking + the `dnssec-policy` `keys` configuration against the configured key + stores. If the found key files are not in sync with the given + `dnssec-policy`, the check will fail. + + This is useful to run before migrating to `dnssec-policy`. :gl:`#5486` + :gl:`!10907` + +Removed Features +~~~~~~~~~~~~~~~~ + +- Remove randomized RRset ordering. ``014a05a2781`` + + The rrset-order random doesn't offer uniform distribution of all + permutations and it isn't superior to cyclic order in any way. Make + the random ordering an alias to the cyclic ordering. :gl:`#5513` + :gl:`!10912` + +- Remove CHECK_FOR_GLUE_IN_ANSWER. ``7fa4cbedc50`` + + Macro CHECK_FOR_GLUE_IN_ANSWER is defined in `lib/dns/resolver.c` + only, documented nowhere and not exposed as build configuration. This + is valid at least for 9.21+, 9.20 and 9.18. Furthermore, it doesn't + compile anymore on 9.21+ with -DCHECK_FOR_GLUE_IN_ANSWER=1. + + Considering it is very unlikely that anyone build named with this, + remove the code rather than fixing it. :gl:`#5538` :gl:`!11029` + +- Remove orphan dns_loadmgr_t type. ``96855b5449f`` + + dns_loadmgr_t typedef is declared but never defines as well as a + pointer of this type in named_server_t. Removing it. :gl:`!10974` + +Feature Changes +~~~~~~~~~~~~~~~ + +- Add a circular reference between slabtops for type and RRSIG(type) + ``a20c8fe74b0`` + + Previously, the slabtops for "type" and its signature was only loosely + coupled and the headers could expire at different time (both TTL and + LRU based expiry). Add a .related member to the slabtop that allows + us to expire the headers in both related headers and also optimize the + lookups because now both slabtops are looked up at the same time. + :gl:`#3396` :gl:`!10985` + +- Refactor view creation/configuration loops in dedicated functions. + ``cb0807be2be`` + + Refactor a bit of `apply_configuration` by extracting (into respective + dedicated function) the logic to build the keystores list, the KASP + list as well as creating the view/zones and configuring those. This is + the next step of MR !10895 and !10901 + + While the code is extracted, some global variables has been changed + into a function parameters which enable to have a clear view of the + dependency of the function, typically, to know if it depends on local + configuration object or runtime "production" object. The end goal (not + in this MR, but later on) is to move as much as possible + initialization logic outside of the exclusive mode. + + As a first step, latest commits move the keystores list, KASP list and + view/zones creation outside of the exclusive mode. (The view/zone + configuration remain in exclusive mode for now, because of a + dependency to the runtime "cachelist". This is the target of a next + MR. + + For the record; while moving the keystores list, KASP list and + view/zone creation doesn't have a significant impact on the time the + exclusive mode is taken (from my experiment on a 1M small zones + instance); moving `configure_views` did have a _massive_ impact + (basically, the time spend in the exclusive mode is then non + calculable). Configuring views outside the exclusive mode needs more + work, which will be done in future MRs. :gl:`#4673` :gl:`!10910` + +- Add option to always build fuzz binaries. ``54c8252c6e2`` + + Currently the fuzzer binaries are only built when someone requests a + fuzzer. This might cause us to inadvertently break fuzzing when + changing function signatures. It also deviates with the behaviour we + had with autotools, where the fuzz binaries were built with make test. + + This commit splits the -Dfuzzing option into two: fuzzing, and + fuzzing-backend. The fuzzing option controls whether the fuzzing + binaries are built. The fuzzing-backend option controls which backend + to use, and defaults to none. If the value none is used the binaries + are built, but no backend is used or guaranteed, which means that the + binaries might be non-functional. :gl:`#5526` :gl:`!10990` + +- Rename cfg_aclconfctx_t variables to aclctx. ``0411142f826`` + + ACL configuration context variables are inconsistently named as + `actx`, `ac`, or `aclconfctx`, which caused confusion during code + reviews. This commit renames all `cfg_aclconfctx_t` variables to + `aclctx`, which is short, consistent, and unambiguous. :gl:`#5530` + :gl:`!11003` + +- Provide more context when registering plugins. ``ac4cf4cce8d`` + + Add a new type, `ns_pluginregister_ctx_t`, which is passed to + `plugin_register()` in place of the `source` parameter. The source + value is now just part of the structure, which also holds a pointer to + the zone origin if the plugin is loaded at a zone level. This + provides more contextual information, enabling the plugin to make + specific configuration decisions based on the name of the zone for + which it is loaded. It's also flexible if more contextual data + are needed in the future: add a new field to + `ns_pluginregister_ctx_t`, and new plugins can use it without + affecting compatibility with existing plugins. :gl:`#5533` + :gl:`!11019` + +- Add option to compile named with static linking and LTO. + ``b6971fb7240`` + + Statically linking lib{isc,dns,ns,cfg,isccc} and enabling LTO shows + over 10% improvements on all almost measurements in perflab. That + said, we can't use Meson's option for LTO since it would result in + every binary being compiled with LTO and a great increase in compile + time. + + To work around it, we add a configuration option that enables LTO and + static linking only for the `named` binary. :gl:`!10761` + +- Convert slabtop and slabheader to use the cds list. ``7443ff330cc`` + + This is the first MR in series that aims to reduce the node locking by + replacing the single-linked list of slabtop(s) and slabheader(s) with + CDS linked list. This commit doesn't do anything else beyond + replacing .next and .down links with the cds_list_head. The RCU + semantics will be added later. :gl:`!10944` + +- Make the database ownercase modifiable only via addrdataset() + ``dbc47312925`` + + Simplify the implementation around the database ownercase. Remove the + dns_rdataset_setownercase() implementation for the slabheaders and + only allow setting ownercase on rdatalists and rdatasets. The + ownercase in the database can now be set only with + dns_db_addrdataset() by passing rdataset with correctly set ownercase. + :gl:`!10971` + +- Minor refactor of dst code. ``f5af3e431b9`` + + Convert the defines to enums. Initialize the tags more explicitly and + less ugly. :gl:`!11000` + +- Rename ns_pluginregister_ctx_t into ns_pluginctx_t. ``029a7152bba`` + + The type `ns_pluginregister_ctx_t` was initially added to pass plugin + contextual data when the plugin is registered, but this is also now + passed into `plugin_check`. Furthermore, those various data are not + specific to the registration in particular. Rename the type into + `ns_pluginctx_t` for clarity. :gl:`!11035` + +- Simplify nchildren count in isc_nm_listenudp. ``722ce92f107`` + + Slight simplification of the logic to define .nchildren listening UDP + socket. :gl:`!10978` + +- Squash the qpcache tree and nsec tries. ``22803b93e3f`` + + The dns_qpcache already had all the namespace changes needed to put + the normal data and auxiliary NSEC data into a single tree. Remove + the extra nsec QP trie and use the single QP trie for all the cache + data. :gl:`!10975` + +- Use lock-free hashtable for storing resolver fetch contexts. + ``0ac744ee4de`` + + Replace the locked hashmap with the lock-free hashtable from the RCU + library and protect the fetch contexts against reuse by replacing the + libisc reference counting with urcu_ref that can soft-fail in + situation where the reference count is already zero. This allows us + to easily skip re-using the fetch context if it is already in process + of being destroyed. :gl:`!10653` + +Bug Fixes +~~~~~~~~~ + +- Use signer name when disabling DNSSEC algorithms. ``7e0318df857`` + + ``disable-algorithms`` could cause DNSSEC validation failures when the + parent zone was signed with the algorithms that were being disabled + for the child zone. This has been fixed; `disable-algorithms` now + works on a whole-of-zone basis. + + If the zone's name is at or below the ``disable-algorithms`` name the + algorithm is disabled for that zone, using deepest match when there + are multiple ``disable-algorithms`` clauses. :gl:`#5165` :gl:`!10837` + +- Rndc sign during ZSK rollover will now replace signatures. + ``6246f9d7cb1`` + + When performing a ZSK rollover, if the new DNSKEY is omnipresent, the + :option:`rndc sign` command now signs the zone completely with the + successor key, replacing all zone signatures from the predecessor key + with new ones. :gl:`#5483` :gl:`!10867` + +- Missing DNSSEC information when CD bit is set in query. + ``5fcc063ce9a`` + + The RRSIGs for glue records were not being cached correctly for CD=1 + queries. This has been fixed. :gl:`#5502` :gl:`!10938` + +- Fix datarace between unlocking fctx lock and shuttingdown fctx. + ``2924f59cb3e`` + + There was a data race where new fetch response could be added to the + fetch context after we unlock the fetch context and before we shut it + down. This could cause assertion failure when fctx__done() was called + with ISC_R_SUCCESS because there was originally no fetch response, but + new fetch response without associated dataset was added before we had + a chance to shutdown the fetch context. This manifested in the + validated() callback, where cache_rrset() now returns ISC_R_SUCCESS + instead of DNS_R_UNCHANGED when cache was not changed. However the + data race was wrong on a general level. + + Add new argument to fctx__done() that allows to call it with + fctx->lock already acquired to prevent these data races. :gl:`#5507` + :gl:`!10961` + +- Add chroot check to meson.build. ``f2f2488bbe1`` + + The meson build procedure was not checking for the existence of the + chroot function. This has been fixed. :gl:`#5519` :gl:`!10973` + +- Preserve cache when reload fails and reload the server again. + ``33bcff46d30`` + + Fixes an issue where failing to reconfigure/reload the server would + prevent to preserved the views caches on the subsequent server + reconfiguration/reload. :gl:`#5523` :gl:`!10984` + +- Apply_configuration: leave exclusive mode after viewlist cleanup. + ``5c53695bf32`` + + When a re-configuration fails, `apply_configuration` flows jump to a + cleanup label and, at some point, leave the exclusive mode and cleanup + the viewlist. It looks fine as the viewlist is at this point only + locally known (if this is a configuration failure, this is the new + view list, if this is a success, this is the old list which has been + swapped out from the production list during the exclusive mode). + + However, the view and zone initialization code enqueues job callbacks, + for instance from `dns_zone_setsigninginterval` (but there are others + cases) which will be called for the new views and zones after the + exclusive mode is over. + + Depending where the configuration fails, those views and zones can be + half-configured, for instance a view might have an unfrozen resolver. + Hence, leaving the exclusive mode before cleaning up those views ans + zones will immediately called the previously enqueued callbacks and + lead to this reconfiguration-failure crash stack: + + ``` isc_assertion_failed dns_resolver_createfetch do_keyfetch + isc__async_cb ... uv_run loop_thread thread_body thread_run + start_thread ... ``` + + To avoid the problem, the views are now cleaned up before leaving the + exclusive mode (which also clean up the zones and enqueued callbacks). + + As context, the bug was introduced by !10910 which moved the creation + (not configuration) of the view outsides of the exclusive mode. This + is a safe move (as at this point, the newly view are only known + locally by `apply_configuration`) but the re-order was wrong regarding + the point where the exclusive mode was ended (before the change, the + exclusive mode as always ended before the new view are detached). + :gl:`!11016` + +- Check plugin config before registering. ``0e575d150fd`` + + In `named_config_parsefile()`, when checking the validity of + `named.conf`, the checking of plugin correctness was deliberately + postponed until the plugin is loaded and registered. However, the + checking was never actually done: the `plugin_register()` + implementation was called, but `plugin_check()` was not. + + `ns_plugin_register()` (used by `named`) now calls the check function + before the register function, and aborts if either one fails. + `ns_plugin_check()` (used by `named-checkconf`) calls only the check + function. :gl:`!11031` + +- Clean up the dns_db API. ``29fc7850f1e`` + + Some of the API calls in `dns_db` were obsolete, and have been + removed. Others were more complicated than necessary, and have been + refactored to simplify. :gl:`!10830` + +- Do not inline dns_zone_gethooktable. ``e7156fe57ae`` + + Since !10959 `dns_zone_gethooktable()` is only called once per query, + and the suspicion (from perflab analysis) that this (simple, as just + returning a pointer) call was slowing things down (perhaps because of + code locality reasons?) doesn't matter anymore. So even if !10959 + inlined it, it shouldn't matter anymore. :gl:`!10962` + +- Fix detection of whether node is active in find_wildcard() + ``f717bad1086`` + + The current code would fail during the write transaction. The first + header would not match the search->serial and the node might be + incorrectly detected as inactive. :gl:`!10972` + +- Hookasyncctx renaming. ``6ec65c3d1ad`` + + The field `ns_hookasync_t` was initially named `hook_actx` and wrongly + renamed `hook_aclctx` during a mass-renaming of various names for the + config acl context into a consistent `aclctx` name (see !11003). Of + course this is wrong as `ns_hookasync_t` has nothing to do with ACL + but about _async_ context. This commit fixes the mistake by renaming + this field `hookasyncctx` :gl:`!11021` + +- Minimize zone hooktable lookups. ``89039e0d78e`` + + Merging !10483 caused a performance regression because the zone + hooktable had to be looked up every time a hook point was reached, + even if no zone plugins were configured. We now look up the zone + hooktable when a zone is attached to the query context, and keep a + pointer to it until the qctx is destroyed. :gl:`!10959` + + diff --git a/doc/notes/notes-9.21.13.rst b/doc/notes/notes-9.21.13.rst new file mode 100644 index 0000000000..65b5372cf4 --- /dev/null +++ b/doc/notes/notes-9.21.13.rst @@ -0,0 +1,18 @@ +.. 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. + +Notes for BIND 9.21.13 +---------------------- + +.. note:: + + The BIND 9.21.13 release was withdrawn after the discovery of a + regression in a security fix in it during pre-release testing. diff --git a/doc/notes/notes-9.21.14.rst b/doc/notes/notes-9.21.14.rst new file mode 100644 index 0000000000..199d481f7a --- /dev/null +++ b/doc/notes/notes-9.21.14.rst @@ -0,0 +1,158 @@ +.. 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. + +Notes for BIND 9.21.14 +---------------------- + +Security Fixes +~~~~~~~~~~~~~~ + +- DNSSEC validation fails if matching but invalid DNSKEY is found. + :cve:`2025-8677` + + Previously, if a matching but cryptographically invalid key was + encountered during DNSSEC validation, the key was skipped and not + counted towards validation failures. :iscman:`named` now treats such + DNSSEC keys as hard failures and the DNSSEC validation fails + immediately, instead of continuing with the next DNSKEYs in the RRset. + + ISC would like to thank Zuyao Xu and Xiang Li from the All-in-One + Security and Privacy Laboratory at Nankai University for bringing this + vulnerability to our attention. :gl:`#5343` + +- Address various spoofing attacks. :cve:`2025-40778` + + Previously, several issues could be exploited to poison a DNS cache + with spoofed records for zones which were not DNSSEC-signed or if the + resolver was configured to not do DNSSEC validation. These issues were + assigned CVE-2025-40778 and have now been fixed. + + As an additional layer of protection, :iscman:`named` no longer + accepts DNAME records or extraneous NS records in the AUTHORITY + section unless these are received via spoofing-resistant transport + (TCP, UDP with DNS cookies, TSIG, or SIG(0)). + + ISC would like to thank Yuxiao Wu, Yunyi Zhang, Baojun Liu, and Haixin + Duan from Tsinghua University for bringing this vulnerability to our + attention. :gl:`#5414` + +- Cache-poisoning due to weak pseudo-random number generator. + :cve:`2025-40780` + + It was discovered during research for an upcoming academic paper that + a xoshiro128\*\* internal state can be recovered by an external 3rd + party, allowing the prediction of UDP ports and DNS IDs in outgoing + queries. This could lead to an attacker spoofing the DNS answers with + great efficiency and poisoning the DNS cache. + + The internal random generator has been changed to a cryptographically + secure pseudo-random generator. + + ISC would like to thank Prof. Amit Klein and Omer Ben Simhon from + Hebrew University of Jerusalem for bringing this vulnerability to our + attention. :gl:`#5484` + +New Features +~~~~~~~~~~~~ + +- Add :any:`dnssec-policy` keys configuration check to + :iscman:`named-checkconf`. + + A new option :option:`-k ` was added to + :iscman:`named-checkconf` that allows checking the + :any:`dnssec-policy` :any:`keys` configuration against the configured + key stores. If the found key files are not in sync with the given + :any:`dnssec-policy`, the check will fail. + + This is useful to run before migrating to :any:`dnssec-policy`. + :gl:`#5486` + +- Add support for synthetic records. + + Add :iscman:`synthrecord` query plugin which, in "reverse" mode, + enables the server to build a synthesized response to a PTR query when + the PTR record requested is not found in the zone. + + The dynamically built name is constructed from a static prefix (passed + as a plugin parameter), the IP address (extracted from the query + name), and a suffix (also passed as a plugin parameter). An + ``allow-synth`` address-match list can be used to limit the network + addresses for which the plugin may generate responses. + + The plugin can also be used in "forward" mode, to build synthesized + A/AAAA records from names using the same format as the dynamically + built PTR names. The same parameters are used: the plugin reacts and + answers a query if the name matches the configured prefix and origin, + and encodes an IP address that is within ``allow-synth``. :gl:`#1586` + +- Support for zone-specific plugins. + + Query plugins can now be configured at the :any:`zone` level, as well + as globally or at the :any:`view` level. A plugin's hooks are then + called only while that specific zone's database is being used to + answer a query. + + This simplifies the implementation of plugins that are only needed for + specific namespaces for which the server is authoritative. It can also + enable quicker responses, since plugins are only called when they are + needed. :gl:`#5356` + +- Support for additional tokens in the zone file name template. + + See :any:`file` for a complete list of currently supported tokens. + :gl:`#85` + +Removed Features +~~~~~~~~~~~~~~~~ + +- Remove randomized RRset ordering. + + :any:`rrset-order` ``random`` did not offer uniform distribution of + all permutations and it was not superior to the ``cyclic`` order in + any way. ``random`` ordering is now an alias for ``cyclic`` ordering. + :gl:`#5513` + +Bug Fixes +~~~~~~~~~ + +- Missing DNSSEC information when CD bit is set in query. + + The RRSIGs for glue records were not being cached correctly for CD=1 + queries. This has been fixed. :gl:`#5502` + +- :option:`rndc sign` during ZSK rollover will now replace signatures. + + When performing a ZSK rollover, if the new DNSKEY is omnipresent, the + :option:`rndc sign` command now signs the zone completely with the + successor key, replacing all zone signatures from the predecessor key + with new ones. :gl:`#5483` + +- Add a check for ``chroot()`` to the build system. + + The Meson build procedure was not checking for the existence of the + ``chroot()`` function. This has been fixed. :gl:`#5519` + +- Use signer name when disabling DNSSEC algorithms. + + :any:`disable-algorithms` could cause DNSSEC validation failures when + the parent zone was signed with the algorithms that were being + disabled for the child zone. This has been fixed; + :any:`disable-algorithms` now works on a whole-of-zone basis. + + If the zone's name is at or below the :any:`disable-algorithms` name + the algorithm is disabled for that zone, using deepest match when + there are multiple :any:`disable-algorithms` clauses. :gl:`#5165` + +- Preserve cache when reload fails and reload the server again. + + This fixes an issue where failing to reconfigure/reload the server + would fail to preserve the views' caches for subsequent server + reconfigurations/reloads. :gl:`#5523` diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index 557a745bbc..bfda3fbb56 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -261,6 +261,7 @@ struct dns_message { unsigned int rdclass_set : 1; /* 14 */ unsigned int fuzzing : 1; /* 15 */ unsigned int free_pools : 1; /* 16 */ + unsigned int has_dname : 1; /* 17 */ unsigned int : 0; unsigned int opt_reserved; @@ -1456,3 +1457,10 @@ dns_message_createpools(isc_mem_t *mctx, isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp); void dns_message_destroypools(isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp); + +bool +dns_message_hasdname(dns_message_t *msg); +/*%< + * Return whether a DNAME was detected in the ANSWER section of a QUERY + * message when it was parsed. + */ diff --git a/lib/dns/message.c b/lib/dns/message.c index e777ed99df..f0f27a9d73 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -442,6 +442,7 @@ msginit(dns_message_t *m) { m->cc_bad = 0; m->tkey = 0; m->rdclass_set = 0; + m->has_dname = 0; m->querytsig = NULL; m->indent.string = "\t"; m->indent.count = 0; @@ -1492,6 +1493,11 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t dctx, */ msg->tsigname->attributes.nocompress = true; free_name = false; + } else if (rdtype == dns_rdatatype_dname && + sectionid == DNS_SECTION_ANSWER && + msg->opcode == dns_opcode_query) + { + msg->has_dname = 1; } rdataset = NULL; @@ -5081,3 +5087,9 @@ dns_message_destroypools(isc_mempool_t **namepoolp, isc_mempool_t **rdspoolp) { isc_mempool_destroy(rdspoolp); isc_mempool_destroy(namepoolp); } + +bool +dns_message_hasdname(dns_message_t *msg) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + return msg->has_dname; +} diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 09b0db084f..f8e76f22e5 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -854,6 +854,7 @@ typedef struct respctx { bool get_nameservers; /* get a new NS rrset at * zone cut? */ bool resend; /* resend this query? */ + bool secured; /* message was signed or had a valid cookie */ bool nextitem; /* invalid response; keep * listening for the correct one */ bool truncated; /* response was truncated */ @@ -6357,7 +6358,8 @@ mark_related(dns_name_t *name, dns_rdataset_t *rdataset, bool external, * locally served zone. */ static inline bool -name_external(const dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { +name_external(const dns_name_t *name, dns_rdatatype_t type, respctx_t *rctx) { + fetchctx_t *fctx = rctx->fctx; isc_result_t result; dns_forwarders_t *forwarders = NULL; dns_name_t *apex = NULL; @@ -6367,7 +6369,7 @@ name_external(const dns_name_t *name, dns_rdatatype_t type, fetchctx_t *fctx) { dns_namereln_t rel; apex = (ISDUALSTACK(fctx->addrinfo) || !ISFORWARDER(fctx->addrinfo)) - ? fctx->domain + ? rctx->ns_name != NULL ? rctx->ns_name : fctx->domain : fctx->fwdname; /* @@ -6464,7 +6466,7 @@ check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type, result = dns_message_findname(rctx->query->rmessage, section, addname, dns_rdatatype_any, 0, &name, NULL); if (result == ISC_R_SUCCESS) { - external = name_external(name, type, fctx); + external = name_external(name, type, rctx); if (type == dns_rdatatype_a) { ISC_LIST_FOREACH(name->list, rdataset, link) { if (dns_rdatatype_issig(rdataset->type)) { @@ -7275,13 +7277,10 @@ rctx_cookiecheck(respctx_t *rctx) { resquery_t *query = rctx->query; /* - * If TSIG signed, sent via TCP, or cookie present, - * no need to continue. + * If the message was secured or TCP is already in the + * retry flags, no need to continue. */ - if (dns_message_gettsig(query->rmessage, NULL) != NULL || - query->rmessage->cc_ok || query->rmessage->cc_bad || - (rctx->retryopts & DNS_FETCHOPT_TCP) != 0) - { + if (rctx->secured || (rctx->retryopts & DNS_FETCHOPT_TCP) != 0) { return ISC_R_SUCCESS; } @@ -7344,6 +7343,47 @@ rctx_cookiecheck(respctx_t *rctx) { return ISC_R_SUCCESS; } +static bool +rctx_need_tcpretry(respctx_t *rctx) { + resquery_t *query = rctx->query; + if ((rctx->retryopts & DNS_FETCHOPT_TCP) != 0) { + /* TCP is already in the retry flags */ + return false; + } + + /* + * If the message was secured, no need to continue. + */ + if (rctx->secured) { + return false; + } + + /* + * Currently the only extra reason why we might need to + * retry a UDP response over TCP is a DNAME in the message. + */ + if (dns_message_hasdname(query->rmessage)) { + return true; + } + + return false; +} + +static isc_result_t +rctx_tcpretry(respctx_t *rctx) { + /* + * Do we need to retry a UDP response over TCP? + */ + if (rctx_need_tcpretry(rctx)) { + rctx->retryopts |= DNS_FETCHOPT_TCP; + rctx->resend = true; + rctx_done(rctx, ISC_R_SUCCESS); + return ISC_R_COMPLETE; + } + + return ISC_R_SUCCESS; +} + static void resquery_response_continue(void *arg, isc_result_t result) { respctx_t *rctx = arg; @@ -7363,6 +7403,17 @@ resquery_response_continue(void *arg, isc_result_t result) { goto cleanup; } + /* + * Remember whether this message was signed or had a + * valid client cookie; if not, we may need to retry over + * TCP later. + */ + if (query->rmessage->cc_ok || query->rmessage->tsig != NULL || + query->rmessage->sig0 != NULL) + { + rctx->secured = true; + } + /* * The dispatcher should ensure we only get responses with QR * set. @@ -7370,13 +7421,21 @@ resquery_response_continue(void *arg, isc_result_t result) { INSIST((query->rmessage->flags & DNS_MESSAGEFLAG_QR) != 0); /* - * Check for cookie issues. + * Check for cookie issues; if found, maybe retry over TCP. */ result = rctx_cookiecheck(rctx); if (result == ISC_R_COMPLETE) { goto cleanup; } + /* + * Check whether we need to retry over TCP for some other reason. + */ + result = rctx_tcpretry(rctx); + if (result == ISC_R_COMPLETE) { + goto cleanup; + } + /* * Check for EDNS issues. */ @@ -8089,8 +8148,8 @@ rctx_answer_positive(respctx_t *rctx) { } /* - * Cache records in the authority section, if - * there are any suitable for caching. + * Cache records in the authority section, if there are + * any suitable for caching. */ rctx_authority_positive(rctx); @@ -8150,7 +8209,7 @@ rctx_answer_scan(respctx_t *rctx) { /* * Don't accept DNAME from parent namespace. */ - if (name_external(name, dns_rdatatype_dname, fctx)) { + if (name_external(name, dns_rdatatype_dname, rctx)) { continue; } @@ -8428,22 +8487,29 @@ rctx_answer_dname(respctx_t *rctx) { /* * rctx_authority_positive(): - * Examine the records in the authority section (if there are any) for a - * positive answer. We expect the names for all rdatasets in this - * section to be subdomains of the domain being queried; any that are - * not are skipped. We expect to find only *one* owner name; any names - * after the first one processed are ignored. We expect to find only - * rdatasets of type NS, RRSIG, or SIG; all others are ignored. Whatever - * remains can be cached at trust level authauthority or additional - * (depending on whether the AA bit was set on the answer). + * If a positive answer was received over TCP or secured with a cookie + * or TSIG, examine the authority section. We expect names for all + * rdatasets in this section to be subdomains of the domain being queried; + * any that are not are skipped. We expect to find only *one* owner name; + * any names after the first one processed are ignored. We expect to find + * only rdatasets of type NS; all others are ignored. Whatever remains can + * be cached at trust level authauthority or additional (depending on + * whether the AA bit was set on the answer). */ static void rctx_authority_positive(respctx_t *rctx) { fetchctx_t *fctx = rctx->fctx; - dns_message_t *msg = rctx->query->rmessage; + + /* If it's spoofable, don't cache it. */ + if (!rctx->secured && (rctx->query->options & DNS_FETCHOPT_TCP) == 0) { + return; + } + MSG_SECTION_FOREACH(msg, DNS_SECTION_AUTHORITY, name) { - if (!name_external(name, dns_rdatatype_ns, fctx)) { + if (!name_external(name, dns_rdatatype_ns, rctx) && + dns_name_issubdomain(fctx->name, name)) + { /* * We expect to find NS or SIG NS rdatasets, and * nothing else. diff --git a/lib/dns/validator.c b/lib/dns/validator.c index b9a6b0f3d9..c6781544b9 100644 --- a/lib/dns/validator.c +++ b/lib/dns/validator.c @@ -366,6 +366,12 @@ trynsec3: static void resume_answer_with_key_done(void *arg); +static bool +over_max_fails(dns_validator_t *val); + +static void +consume_validation_fail(dns_validator_t *val); + static void resume_answer_with_key(void *arg) { dns_validator_t *val = arg; @@ -374,6 +380,13 @@ resume_answer_with_key(void *arg) { isc_result_t result = select_signing_key(val, rdataset); if (result == ISC_R_SUCCESS) { val->keyset = &val->frdataset; + } else if (result != ISC_R_NOTFOUND) { + val->result = result; + if (over_max_fails(val)) { + INSIST(val->key == NULL); + val->result = ISC_R_QUOTA; + } + consume_validation_fail(val); } (void)validate_async_run(val, resume_answer_with_key_done); @@ -383,6 +396,16 @@ static void resume_answer_with_key_done(void *arg) { dns_validator_t *val = arg; + switch (val->result) { + case ISC_R_CANCELED: /* Validation was canceled */ + case ISC_R_SHUTTINGDOWN: /* Server shutting down */ + case ISC_R_QUOTA: /* Validation fails quota reached */ + dns_validator_cancel(val); + break; + default: + break; + } + resume_answer(val); } @@ -1047,9 +1070,6 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) { val->key = NULL; result = dns_rdataset_next(rdataset); } - if (result == ISC_R_NOMORE) { - return ISC_R_NOTFOUND; - } for (; result == ISC_R_SUCCESS; result = dns_rdataset_next(rdataset)) { dns_rdata_rrsig_t *siginfo = val->siginfo; @@ -1072,18 +1092,11 @@ select_signing_key(dns_validator_t *val, dns_rdataset_t *rdataset) { continue; } - result = dns_dnssec_keyfromrdata(&siginfo->signer, &rdata, - val->view->mctx, &val->key); - if (result == ISC_R_SUCCESS) { - /* found the key we wanted */ - break; - } - } - if (result == ISC_R_NOMORE) { - result = ISC_R_NOTFOUND; + return dns_dnssec_keyfromrdata(&siginfo->signer, &rdata, + val->view->mctx, &val->key); } - return result; + return ISC_R_NOTFOUND; } /*% @@ -1311,6 +1324,7 @@ selfsigned_dnskey(dns_validator_t *val) { dns_name_t *name = val->name; isc_result_t result; isc_mem_t *mctx = val->view->mctx; + bool match = false; if (rdataset->type != dns_rdatatype_dnskey) { return DNS_R_NOKEYMATCH; @@ -1344,17 +1358,16 @@ selfsigned_dnskey(dns_validator_t *val) { /* * If the REVOKE bit is not set we have a - * theoretically self signed DNSKEY RRset. - * This will be verified later. + * theoretically self-signed DNSKEY RRset; + * this will be verified later. + * + * We don't return the answer yet, though, + * because we need to check the remaining keys + * and possbly remove them if they're revoked. */ if ((key.flags & DNS_KEYFLAG_REVOKE) == 0) { - return ISC_R_SUCCESS; - } - - result = dns_dnssec_keyfromrdata(name, &keyrdata, mctx, - &dstkey); - if (result != ISC_R_SUCCESS) { - continue; + match = true; + break; } /* @@ -1364,6 +1377,20 @@ selfsigned_dnskey(dns_validator_t *val) { if (DNS_TRUST_PENDING(rdataset->trust) && dns_view_istrusted(val->view, name, &key)) { + result = dns_dnssec_keyfromrdata( + name, &keyrdata, mctx, &dstkey); + if (result == DST_R_UNSUPPORTEDALG) { + /* don't count towards max fails */ + break; /* continue with next key */ + } else if (result != ISC_R_SUCCESS) { + consume_validation(val); + if (over_max_fails(val)) { + return ISC_R_QUOTA; + } + consume_validation_fail(val); + break; /* continue with next key */ + } + if (over_max_validations(val)) { dst_key_free(&dstkey); return ISC_R_QUOTA; @@ -1397,6 +1424,8 @@ selfsigned_dnskey(dns_validator_t *val) { consume_validation_fail(val); break; } + + dst_key_free(&dstkey); } else if (rdataset->trust >= dns_trust_secure) { /* * We trust this RRset so if the key is @@ -1404,12 +1433,14 @@ selfsigned_dnskey(dns_validator_t *val) { */ dns_view_untrust(val->view, name, &key); } - - dst_key_free(&dstkey); } } - return DNS_R_NOKEYMATCH; + if (!match) { + return DNS_R_NOKEYMATCH; + } + + return ISC_R_SUCCESS; } /*% @@ -1594,7 +1625,7 @@ validate_answer_signing_key_done(void *arg); static void validate_answer_signing_key(void *arg) { dns_validator_t *val = arg; - isc_result_t result = ISC_R_NOTFOUND; + isc_result_t result; if (CANCELED(val) || CANCELING(val)) { val->result = ISC_R_CANCELED; @@ -1617,15 +1648,21 @@ validate_answer_signing_key(void *arg) { default: /* Select next signing key */ result = select_signing_key(val, val->keyset); + if (result == ISC_R_SUCCESS) { + INSIST(val->key != NULL); + } else if (result == ISC_R_NOTFOUND) { + INSIST(val->key == NULL); + } else { + val->result = result; + if (over_max_fails(val)) { + INSIST(val->key == NULL); + val->result = ISC_R_QUOTA; + } + consume_validation_fail(val); + } break; } - if (result == ISC_R_SUCCESS) { - INSIST(val->key != NULL); - } else { - INSIST(val->key == NULL); - } - (void)validate_async_run(val, validate_answer_signing_key_done); } diff --git a/lib/isc/entropy.c b/lib/isc/entropy.c deleted file mode 100644 index a037960bd1..0000000000 --- a/lib/isc/entropy.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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. - */ - -#include -#include -#include -#include - -void -isc_entropy_get(void *buf, size_t buflen) { - int r = uv_random(NULL, NULL, buf, buflen, 0, NULL); - - UV_RUNTIME_CHECK(uv_random, r); -} diff --git a/lib/isc/hash.c b/lib/isc/hash.c index 387e2373cd..49b942ef5b 100644 --- a/lib/isc/hash.c +++ b/lib/isc/hash.c @@ -16,7 +16,6 @@ #include #include -#include #include /* IWYU pragma: keep */ #include #include @@ -35,7 +34,7 @@ isc__hash_initialize(void) { */ uint8_t key[16] = { 1 }; #if !FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - isc_entropy_get(key, sizeof(key)); + isc_random_buf(key, sizeof(key)); #endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ STATIC_ASSERT(sizeof(key) >= sizeof(isc_hash_key), "sizeof(key) < sizeof(isc_hash_key)"); diff --git a/lib/isc/hashmap.c b/lib/isc/hashmap.c index d8c2bd58ae..87b1a3f21a 100644 --- a/lib/isc/hashmap.c +++ b/lib/isc/hashmap.c @@ -32,7 +32,6 @@ #include #include -#include #include #include #include diff --git a/lib/isc/include/isc/entropy.h b/lib/isc/include/isc/entropy.h deleted file mode 100644 index 81c2ed7cac..0000000000 --- a/lib/isc/include/isc/entropy.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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. - */ - -#pragma once - -#include - -/*! \file isc/entropy.h - * \brief Implements wrapper around CSPRNG cryptographic library calls - * for getting cryptographically secure pseudo-random numbers. - * - * Uses synchronous version of uv_random(). - */ - -void -isc_entropy_get(void *buf, size_t buflen); -/*!< - * \brief Get cryptographically-secure pseudo-random data. - */ diff --git a/lib/isc/include/isc/nonce.h b/lib/isc/include/isc/nonce.h index ce15b7e12a..b61270bd82 100644 --- a/lib/isc/include/isc/nonce.h +++ b/lib/isc/include/isc/nonce.h @@ -15,12 +15,16 @@ #include +#include + /*! \file isc/nonce.h * \brief Provides a function for generating an arbitrarily long nonce. */ -void -isc_nonce_buf(void *buf, size_t buflen); +static inline void +isc_nonce_buf(void *buf, size_t buflen) { + isc_random_buf(buf, buflen); +} /*!< * Fill 'buf', up to 'buflen' bytes, with random data from the * crypto provider's random function. diff --git a/lib/isc/include/isc/random.h b/lib/isc/include/isc/random.h index 213e979ec6..efee399095 100644 --- a/lib/isc/include/isc/random.h +++ b/lib/isc/include/isc/random.h @@ -19,23 +19,16 @@ #include /*! \file isc/random.h - * \brief Implements wrapper around a non-cryptographically secure + * \brief Implements wrapper around a cryptographically secure * pseudo-random number generator. * */ -uint8_t -isc_random8(void); -/*!< - * \brief Returns a single 8-bit random value. - */ - -uint16_t -isc_random16(void); -/*!< - * \brief Returns a single 16-bit random value. - */ - +#if HAVE_ARC4RANDOM && !defined(__linux__) +#define isc_random32() arc4random() +#define isc_random_buf(buf, buflen) arc4random_buf(buf, buflen) +#define isc_random_uniform(upper_bound) arc4random_uniform(upper_bound) +#else /* HAVE_ARC4RANDOM && !defined(__linux__) */ uint32_t isc_random32(void); /*!< @@ -64,3 +57,21 @@ isc_random_uniform(uint32_t upper_bound); * resample is very small when the upper_bound is small, rising to 0.5 * when upper_bound is UINT32_MAX/2. */ + +#endif /* HAVE_ARC4RANDOM && !defined(__linux__) */ + +static inline uint8_t +isc_random8(void) { + return (uint8_t)isc_random32(); +} +/*!< + * \brief Returns a single 8-bit random value. + */ + +static inline uint16_t +isc_random16(void) { + return (uint16_t)isc_random32(); +} +/*!< + * \brief Returns a single 16-bit random value. + */ diff --git a/lib/isc/meson.build b/lib/isc/meson.build index 0e8e9cf0b3..d4db21672e 100644 --- a/lib/isc/meson.build +++ b/lib/isc/meson.build @@ -77,7 +77,6 @@ isc_srcset.add( 'counter.c', 'crypto.c', 'dir.c', - 'entropy.c', 'errno.c', 'errno2result.c', 'error.c', @@ -106,7 +105,6 @@ isc_srcset.add( 'net.c', 'netaddr.c', 'netscope.c', - 'nonce.c', 'openssl_shim.c', 'os.c', 'parseint.c', diff --git a/lib/isc/nonce.c b/lib/isc/nonce.c deleted file mode 100644 index 316498a613..0000000000 --- a/lib/isc/nonce.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * 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. - */ - -#include -#include - -void -isc_nonce_buf(void *buf, size_t buflen) { - isc_entropy_get(buf, buflen); -} diff --git a/lib/isc/random.c b/lib/isc/random.c index 88efd21c4e..1367fd5a12 100644 --- a/lib/isc/random.c +++ b/lib/isc/random.c @@ -30,126 +30,55 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#if !HAVE_ARC4RANDOM || defined(__linux__) + #include -#include -#include -#include +#include -#include +#include #include -#include #include -#include #include +#include -/* - * Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) - * - * To the extent possible under law, the author has dedicated all - * copyright and related and neighboring rights to this software to the - * public domain worldwide. This software is distributed without any - * warranty. - * - * See . - */ +#define ISC_RANDOM_BUFSIZE (ISC_OS_CACHELINE_SIZE / sizeof(uint32_t)) -/* - * This is xoshiro128** 1.0, our 32-bit all-purpose, rock-solid generator. - * It has excellent (sub-ns) speed, a state size (128 bits) that is large - * enough for mild parallelism, and it passes all tests we are aware of. - * - * The state must be seeded so that it is not everywhere zero. - */ - -static thread_local bool initialized = false; -static thread_local uint32_t seed[4] = { 0 }; - -static uint32_t -rotl(const uint32_t x, int k) { - return (x << k) | (x >> (32 - k)); -} - -static uint32_t -next(void) { - uint32_t result_starstar, t; - - result_starstar = rotl(seed[0] * 5, 7) * 9; - t = seed[1] << 9; - - seed[2] ^= seed[0]; - seed[3] ^= seed[1]; - seed[1] ^= seed[2]; - seed[0] ^= seed[3]; - - seed[2] ^= t; - - seed[3] = rotl(seed[3], 11); - - return result_starstar; -} - -static void -isc__random_initialize(void) { - if (initialized) { - return; - } - -#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - /* - * A fixed seed helps with problem reproduction when fuzzing. It must be - * non-zero else xoshiro128starstar will generate only zeroes, and the - * first result needs to be non-zero as expected by random_test.c - */ - seed[0] = 1; -#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ - - while (seed[0] == 0 && seed[1] == 0 && seed[2] == 0 && seed[3] == 0) { - isc_entropy_get(seed, sizeof(seed)); - } - initialized = true; -} - -uint8_t -isc_random8(void) { - isc__random_initialize(); - return (uint8_t)next(); -} - -uint16_t -isc_random16(void) { - isc__random_initialize(); - return (uint16_t)next(); -} +thread_local static uint32_t isc__random_pool[ISC_RANDOM_BUFSIZE]; +thread_local static size_t isc__random_pos = ISC_RANDOM_BUFSIZE; uint32_t isc_random32(void) { - isc__random_initialize(); - return next(); +#if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + /* + * A fixed stream of numbers helps with problem reproduction when + * fuzzing. The first result needs to be non-zero as expected by + * random_test.c (it starts with ISC_RANDOM_BUFSIZE, see above). + */ + return (uint32_t)(isc__random_pos++); +#endif /* if FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */ + + if (isc__random_pos == ISC_RANDOM_BUFSIZE) { + isc_random_buf(isc__random_pool, sizeof(isc__random_pool)); + isc__random_pos = 0; + } + + return isc__random_pool[isc__random_pos++]; } void isc_random_buf(void *buf, size_t buflen) { - REQUIRE(buf != NULL); - REQUIRE(buflen > 0); + REQUIRE(buflen == 0 || buf != NULL); - int i; - uint32_t r; - - isc__random_initialize(); - - for (i = 0; i + sizeof(r) <= buflen; i += sizeof(r)) { - r = next(); - memmove((uint8_t *)buf + i, &r, sizeof(r)); + if (buf == NULL || buflen == 0) { + return; } - r = next(); - memmove((uint8_t *)buf + i, &r, buflen % sizeof(r)); - return; + + int r = uv_random(NULL, NULL, buf, buflen, 0, NULL); + UV_RUNTIME_CHECK(uv_random, r); } uint32_t isc_random_uniform(uint32_t limit) { - isc__random_initialize(); - /* * Daniel Lemire's nearly-divisionless unbiased bounded random numbers. * @@ -161,7 +90,7 @@ isc_random_uniform(uint32_t limit) { * integer part (upper 32 bits), and we will use the fraction part * (lower 32 bits) to determine whether or not we need to resample. */ - uint64_t num = (uint64_t)next() * (uint64_t)limit; + uint64_t num = (uint64_t)isc_random32() * (uint64_t)limit; /* * In the fast path, we avoid doing a division in most cases by * comparing the fraction part of `num` with the limit, which is @@ -213,7 +142,7 @@ isc_random_uniform(uint32_t limit) { * our valid range, it is superfluous, and we resample. */ while ((uint32_t)(num) < residue) { - num = (uint64_t)next() * (uint64_t)limit; + num = (uint64_t)isc_random32() * (uint64_t)limit; } } /* @@ -221,3 +150,5 @@ isc_random_uniform(uint32_t limit) { */ return (uint32_t)(num >> 32); } + +#endif /* HAVE_ARC4RANDOM && !defined(__linux__) */ diff --git a/tests/isc/random_test.c b/tests/isc/random_test.c index 70bd4b6a79..a8cce6ceca 100644 --- a/tests/isc/random_test.c +++ b/tests/isc/random_test.c @@ -321,7 +321,9 @@ random_test(pvalue_func_t *func, isc_random_func test_func) { } break; case ISC_RANDOM_BYTES: - isc_random_buf(values, sizeof(values)); + for (i = 0; i < ARRAY_SIZE(values); i++) { + values[i] = isc_random32(); + } break; case ISC_RANDOM_UNIFORM: uniform_values = (uint16_t *)values;