From: Miod Vallat Date: Mon, 22 Sep 2025 08:42:47 +0000 (+0200) Subject: Perform character set validation of view names. X-Git-Tag: auth-5.0.1~10^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=refs%2Fpull%2F16210%2Fhead;p=thirdparty%2Fpdns.git Perform character set validation of view names. Signed-off-by: Miod Vallat (cherry picked from commit b260728c8517d205636471e42deb3b8ffb7db664) --- diff --git a/docs/views.rst b/docs/views.rst index f28ddba880..3fb83ef5f7 100644 --- a/docs/views.rst +++ b/docs/views.rst @@ -85,6 +85,10 @@ that view, as their variantless contents. Only one variant per zone may appear in a view; setting a new zone variant will replace the previous one in the view. +View names are case-sensitive and may be composed of letters, digits, spaces, +as well as `-` (dash), `.` (dot) and `_` (underscore). They are not allowed to +start with a dot or a space. + Resolution Algorithm -------------------- diff --git a/meson.build b/meson.build index 4b6aa8b850..9763f84b04 100644 --- a/meson.build +++ b/meson.build @@ -497,6 +497,8 @@ common_sources += files( src_dir / 'bindparserclasses.hh', src_dir / 'burtle.hh', src_dir / 'cachecleaner.hh', + src_dir / 'check-zone.cc', + src_dir / 'check-zone.hh', src_dir / 'circular_buffer.hh', src_dir / 'comment.hh', src_dir / 'communicator.cc', diff --git a/pdns/Makefile.am b/pdns/Makefile.am index 8dd8e5c3f7..ac6c0c3362 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -204,6 +204,7 @@ pdns_server_SOURCES = \ bindparser.cc \ burtle.hh \ cachecleaner.hh \ + check-zone.cc check-zone.hh \ circular_buffer.hh \ comment.hh \ communicator.cc communicator.hh \ @@ -344,6 +345,7 @@ pdnsutil_SOURCES = \ bindlexer.l \ bindparser.yy \ cachecleaner.hh \ + check-zone.cc check-zone.hh \ circular_buffer.hh \ credentials.cc credentials.hh \ dbdnsseckeeper.cc \ diff --git a/pdns/check-zone.cc b/pdns/check-zone.cc new file mode 100644 index 0000000000..5d6ba5494c --- /dev/null +++ b/pdns/check-zone.cc @@ -0,0 +1,56 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "dns.hh" +#include "dnsrecords.hh" + +#include "check-zone.hh" + +namespace Check +{ + +bool validateViewName(std::string_view name, std::string& error) +{ + if (name.empty()) { + error = "Empty view names are not allowed"; + return false; + } + + if (auto pos = name.find_first_not_of("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 _-."); pos != std::string_view::npos) { + error = std::string("View name contains forbidden character '") + name[pos] + "' at position " + std::to_string(pos); + return false; + } + + if (name[0] == '.') { + error = "View names are not allowed to start with a dot"; + return false; + } + + if (name[0] == ' ') { + error = "View names are not allowed to start with a space"; + return false; + } + + return true; +} + +} // namespace Check diff --git a/pdns/check-zone.hh b/pdns/check-zone.hh new file mode 100644 index 0000000000..7009ad575e --- /dev/null +++ b/pdns/check-zone.hh @@ -0,0 +1,37 @@ +/* + * This file is part of PowerDNS or dnsdist. + * Copyright -- PowerDNS.COM B.V. and its contributors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * In addition, for the avoidance of any doubt, permission is granted to + * link this program with OpenSSL and to (re)distribute the binaries + * produced as the result of such linking. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// These validation/verification routines are used by both pdnsutil and +// the pdns_server REST API. +// They build error messages, if any, into an object provided by the caller +// (preferrably a container if it makes sense to report multiple errors); +// it's up to each caller to decide how to report such errors. + +namespace Check +{ + +// Validate a view name. Although view names never appear on the wire, we +// restrict them to [a-zA-Z0-9-_. ], with empty names or names with leading +// whitespace or a leading dot forbidden. +bool validateViewName(std::string_view name, std::string& error); + +} // namespace Check diff --git a/pdns/pdnsutil.cc b/pdns/pdnsutil.cc index 1f4517c9c7..b67c84ea51 100644 --- a/pdns/pdnsutil.cc +++ b/pdns/pdnsutil.cc @@ -12,6 +12,7 @@ #include #include +#include "check-zone.hh" #include "credentials.hh" #include "dnsseckeeper.hh" #include "dnssecinfra.hh" @@ -5225,6 +5226,11 @@ static int listView(vector& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + return 1; + } + vector ret; B.viewListZones(cmds.at(0), ret); @@ -5242,6 +5248,12 @@ static int listViews(vector& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + // Don't complain about the lack of view support in this case, but + // don't list anything either. + return 0; + } + vector ret; B.viewList(ret); @@ -5259,7 +5271,17 @@ static int viewAddZone(vector& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + return 1; + } + string view{cmds.at(0)}; + string error; + if (!Check::validateViewName(view, error)) { + cerr << error << "." << endl; + return 1; + } ZoneName zone{cmds.at(1)}; if (!B.viewAddZone(view, zone)) { cerr<<"Operation failed."<& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + return 1; + } + string view{cmds.at(0)}; + string error; + if (!Check::validateViewName(view, error)) { + cerr << error << "." << endl; + return 1; + } ZoneName zone{cmds.at(1)}; if (!B.viewDelZone(view, zone)) { cerr<<"Operation failed."<& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + return 1; + } + vector > ret; B.networkList(ret); @@ -5318,6 +5355,11 @@ static int setNetwork(vector& cmds, const std::string_view synopsis) UtilBackend B("default"); //NOLINT(readability-identifier-length) + if ((B.getCapabilities() & DNSBackend::CAP_VIEWS) == 0) { + cerr << "None of the configured backends support views." << endl; + return 1; + } + Netmask net{cmds.at(0)}; string view{}; if (cmds.size() > 1) { diff --git a/pdns/ws-auth.cc b/pdns/ws-auth.cc index 47ac61eecd..c2e82b6b75 100644 --- a/pdns/ws-auth.cc +++ b/pdns/ws-auth.cc @@ -53,6 +53,7 @@ #include "auth-zonecache.hh" #include "threadname.hh" #include "tsigutils.hh" +#include "check-zone.hh" using json11::Json; @@ -2751,6 +2752,10 @@ static void apiServerViewsPOST(HttpRequest* req, HttpResponse* resp) throw ApiException("Zone " + zonename.toString() + " does not exist"); } std::string view{req->parameters["view"]}; + std::string error; + if (!Check::validateViewName(view, error)) { + throw ApiException(error); + } if (!domainInfo.backend->viewAddZone(view, zonename)) { throw ApiException("Failed to add " + zonename.toString() + " to view " + view); @@ -2775,6 +2780,10 @@ static void apiServerViewsDELETE(HttpRequest* req, HttpResponse* resp) { ZoneData zoneData{req}; std::string view{req->parameters["view"]}; + std::string error; + if (!Check::validateViewName(view, error)) { + throw ApiException(error); + } if (!zoneData.domainInfo.backend->viewDelZone(view, zoneData.zoneName)) { throw ApiException("Failed to remove " + zoneData.zoneName.toString() + " from view " + view); diff --git a/regression-tests/tests/views-management/expected_result b/regression-tests/tests/views-management/expected_result index 966b8a2fdd..dc26423849 100644 --- a/regression-tests/tests/views-management/expected_result +++ b/regression-tests/tests/views-management/expected_result @@ -1 +1 @@ -Operation failed. +None of the configured backends support views.