From: Remi Gacogne Date: Tue, 30 Mar 2021 15:56:45 +0000 (+0200) Subject: Support hashed credentials (password, API key) in the auth and rec X-Git-Tag: dnsdist-1.7.0-alpha1~12^2~36 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=40422f78a1293709b63b7ea7a9eefd5f9b95302a;p=thirdparty%2Fpdns.git Support hashed credentials (password, API key) in the auth and rec --- diff --git a/pdns/Makefile.am b/pdns/Makefile.am index a17aa95e0a..6e14a2effa 100644 --- a/pdns/Makefile.am +++ b/pdns/Makefile.am @@ -200,6 +200,7 @@ pdns_server_SOURCES = \ comment.hh \ common_startup.cc common_startup.hh \ communicator.cc communicator.hh \ + credentials.cc credentials.hh \ dbdnsseckeeper.cc \ digests.hh \ distributor.hh \ @@ -332,6 +333,7 @@ pdnsutil_SOURCES = \ bindparser.yy \ cachecleaner.hh \ circular_buffer.hh \ + credentials.cc credentials.hh \ dbdnsseckeeper.cc \ dns.cc \ dns_random.cc \ @@ -675,6 +677,7 @@ ixfrdist_SOURCES = \ axfr-retriever.cc \ base32.cc \ base64.cc base64.hh \ + credentials.cc credentials.hh \ dns.cc \ dns_random_urandom.cc dns_random.hh \ dnslabeltext.cc \ @@ -709,7 +712,6 @@ ixfrdist_SOURCES = \ webserver.hh webserver.cc \ zoneparser-tng.cc - ixfrdist_LDADD = \ $(BOOST_PROGRAM_OPTIONS_LIBS) \ $(JSON11_LIBS) \ @@ -722,6 +724,10 @@ ixfrdist_LDFLAGS = \ $(BOOST_PROGRAM_OPTIONS_LDFLAGS) \ $(LIBCRYPTO_LDFLAGS) +if LIBSODIUM +ixfrdist_LDADD += $(LIBSODIUM_LIBS) +endif + if PKCS11 ixfrdist_SOURCES += pkcs11signers.cc pkcs11signers.hh ixfrdist_LDADD += $(P11KIT1_LIBS) diff --git a/pdns/recursordist/Makefile.am b/pdns/recursordist/Makefile.am index e9b9b43a4a..39a5fb3c24 100644 --- a/pdns/recursordist/Makefile.am +++ b/pdns/recursordist/Makefile.am @@ -105,6 +105,7 @@ pdns_recursor_SOURCES = \ capabilities.cc capabilities.hh \ circular_buffer.hh \ comment.hh \ + credentials.cc credentials.hh \ dns.hh dns.cc \ dns_random.hh dns_random.cc \ dnsbackend.hh \ @@ -365,6 +366,9 @@ if LIBSODIUM pdns_recursor_SOURCES += \ sodiumsigners.cc pdns_recursor_LDADD += $(LIBSODIUM_LIBS) + +rec_control_LDADD = $(LIBSODIUM_LIBS) + testrunner_SOURCES += \ sodiumsigners.cc testrunner_LDADD += $(LIBSODIUM_LIBS) @@ -439,6 +443,7 @@ endif rec_control_SOURCES = \ arguments.cc arguments.hh \ + credentials.cc credentials.hh \ dnslabeltext.cc \ dnsname.hh dnsname.cc \ logger.cc \ diff --git a/pdns/recursordist/credentials.cc b/pdns/recursordist/credentials.cc new file mode 120000 index 0000000000..385b2128df --- /dev/null +++ b/pdns/recursordist/credentials.cc @@ -0,0 +1 @@ +../credentials.cc \ No newline at end of file diff --git a/pdns/recursordist/credentials.hh b/pdns/recursordist/credentials.hh new file mode 120000 index 0000000000..2c60bfff8f --- /dev/null +++ b/pdns/recursordist/credentials.hh @@ -0,0 +1 @@ +../credentials.hh \ No newline at end of file diff --git a/pdns/webserver.cc b/pdns/webserver.cc index 26633811c0..90ae04043d 100644 --- a/pdns/webserver.cc +++ b/pdns/webserver.cc @@ -52,10 +52,10 @@ json11::Json HttpRequest::json() return doc; } -bool HttpRequest::compareAuthorization(const string &expected_password) +bool HttpRequest::compareAuthorization(const CredentialsHolder& credentials) const { // validate password - YaHTTP::strstr_map_t::iterator header = headers.find("authorization"); + auto header = headers.find("authorization"); bool auth_ok = false; if (header != headers.end() && toLower(header->second).find("basic ") == 0) { string cookie = header->second.substr(6); @@ -66,22 +66,32 @@ bool HttpRequest::compareAuthorization(const string &expected_password) vector cparts; stringtok(cparts, plain, ":"); - // this gets rid of terminating zeros - auth_ok = (cparts.size()==2 && (0==strcmp(cparts[1].c_str(), expected_password.c_str()))); + auth_ok = (cparts.size() == 2 && credentials.matches(cparts[1].c_str())); } return auth_ok; } -bool HttpRequest::compareHeader(const string &header_name, const string &expected_value) +bool HttpRequest::compareHeader(const string &header_name, const string &expected_value) const { - YaHTTP::strstr_map_t::iterator header = headers.find(header_name); - if (header == headers.end()) + auto header = headers.find(header_name); + if (header == headers.end()) { return false; + } // this gets rid of terminating zeros return (0==strcmp(header->second.c_str(), expected_value.c_str())); } +bool HttpRequest::compareHeader(const string &header_name, const CredentialsHolder& credentials) const +{ + auto header = headers.find(header_name); + if (header == headers.end()) { + return false; + } + + return credentials.matches(header->second); +} + void HttpResponse::setPlainBody(const string& document) { this->headers["Content-Type"] = "text/plain; charset=utf-8"; @@ -153,16 +163,16 @@ void WebServer::apiWrapper(const WebServer::HandlerFunction& handler, HttpReques resp->headers["access-control-allow-origin"] = "*"; - if (d_apikey.empty()) { + if (!d_apikey) { g_log<logprefix<<"HTTP API Request \"" << req->url.path << "\": Authentication failed, API Key missing in config" << endl; throw HttpUnauthorizedException("X-API-Key"); } - bool auth_ok = req->compareHeader("x-api-key", d_apikey) || req->getvars["api-key"] == d_apikey; + bool auth_ok = req->compareHeader("x-api-key", *d_apikey) || d_apikey->matches(req->getvars["api-key"]); if (!auth_ok && allowPassword) { - if (!d_webserverPassword.empty()) { - auth_ok = req->compareAuthorization(d_webserverPassword); + if (d_webserverPassword) { + auth_ok = req->compareAuthorization(*d_webserverPassword); } else { auth_ok = true; } @@ -205,8 +215,8 @@ void WebServer::registerApiHandler(const string& url, const HandlerFunction& han } void WebServer::webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp) { - if (!d_webserverPassword.empty()) { - bool auth_ok = req->compareAuthorization(d_webserverPassword); + if (d_webserverPassword) { + bool auth_ok = req->compareAuthorization(*d_webserverPassword); if (!auth_ok) { g_log<logprefix<<"HTTP Request \"" << req->url.path << "\": Web Authentication failed" << endl; throw HttpUnauthorizedException("Basic"); diff --git a/pdns/webserver.hh b/pdns/webserver.hh index 858b56e9eb..2fff9c5009 100644 --- a/pdns/webserver.hh +++ b/pdns/webserver.hh @@ -25,7 +25,10 @@ #include #include #include + #include "json11.hpp" + +#include "credentials.hh" #include "namespaces.hh" #include "sstuff.hh" @@ -42,8 +45,9 @@ public: json11::Json json(); // checks password _only_. - bool compareAuthorization(const string &expected_password); - bool compareHeader(const string &header_name, const string &expected_value); + bool compareAuthorization(const CredentialsHolder& expectedCredentials) const; + bool compareHeader(const string &header_name, const CredentialsHolder& expectedCredentials) const; + bool compareHeader(const string &header_name, const string &expected_value) const; }; class HttpResponse: public YaHTTP::Response { @@ -160,11 +164,21 @@ public: virtual ~WebServer() { }; void setApiKey(const string &apikey) { - d_apikey = apikey; + if (!apikey.empty()) { + d_apikey = make_unique(std::string(apikey)); + } + else { + d_apikey.reset(); + } } void setPassword(const string &password) { - d_webserverPassword = password; + if (!password.empty()) { + d_webserverPassword = make_unique(std::string(password)); + } + else { + d_webserverPassword.reset(); + } } void setMaxBodySize(ssize_t s) { // in megabytes @@ -227,15 +241,15 @@ protected: return std::make_shared(d_listenaddress, d_port); } + void apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword); + void webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp); + string d_listenaddress; int d_port; - string d_password; std::shared_ptr d_server; - std::string d_apikey; - void apiWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp, bool allowPassword); - std::string d_webserverPassword; - void webWrapper(const WebServer::HandlerFunction& handler, HttpRequest* req, HttpResponse* resp); + std::unique_ptr d_apikey{nullptr}; + std::unique_ptr d_webserverPassword{nullptr}; ssize_t d_maxbodysize; // in bytes