]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
rec_control, pdnsutil: Read the credentials from the terminal/stdin
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 9 Apr 2021 13:15:09 +0000 (15:15 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 Sep 2021 12:12:27 +0000 (14:12 +0200)
docs/manpages/pdnsutil.1.rst
docs/settings.rst
pdns/credentials.cc
pdns/credentials.hh
pdns/pdnsutil.cc
pdns/rec_control.cc
pdns/recursordist/docs/manpages/rec_control.1.rst
pdns/recursordist/docs/settings.rst

index 2e141cd8d16ded5a86ce24d9e98e344ccf7a8342..12285832d0bd797d16bdde9953b38ac4236368e1 100644 (file)
@@ -189,10 +189,9 @@ edit-zone *ZONE*
     **EDITOR** is empty, *pdnsutil* falls back to using *editor*.
 get-meta *ZONE* [*ATTRIBUTE*]...
     Get zone metadata. If no *ATTRIBUTE* given, lists all known.
-hash-password *PASSWORD*
-    This convenience command returns a hashed and salted version of the
-    password passed in parameter, for use as a webserver password or
-    api key.
+hash-password
+    This convenience command asks for a password and returns a hashed
+    and salted version, for use as a webserver password or api key.
 hash-zone-record *ZONE* *RNAME*
     This convenience command hashes the name *RNAME* according to the
     NSEC3 settings of *ZONE*. Refuses to hash for zones with no NSEC3
index b8a7c0beda09261193adea47721e0f06f711d981..423a588afc73c6643e5e48333434d5e52f685ea2 100644 (file)
@@ -137,7 +137,7 @@ Enable/disable the :doc:`http-api/index`.
 .. versionchanged:: 4.6.0
   This setting now accepts a hashed and salted version.
 
-Static pre-shared authentication key for access to the REST API. Since 4.6.0 the key can be hashed and salted using ``rec_control hash-password APIKEY`` instead of being stored in the configuration in plaintext.
+Static pre-shared authentication key for access to the REST API. Since 4.6.0 the key can be hashed and salted using ``pdnsutil hash-password`` instead of being stored in the configuration in plaintext.
 
 .. _setting-autosecondary:
 
@@ -1844,7 +1844,7 @@ Maximum request/response body size in megabytes.
 
 -  String
 
-Password required to access the webserver. Since 4.6.0 the password can be hashed and salted using ``pdnsutil hash-password PASS`` instead of being in plaintext.
+Password required to access the webserver. Since 4.6.0 the password can be hashed and salted using ``pdnsutil hash-password`` instead of being in plaintext.
 
 .. _setting-webserver-port:
 
index 139e6337ee720306b338c72d2aa3ed106e34997d..980e5d3dd04fe36ea940f9cde8f65831737428fc 100644 (file)
 #include <sodium.h>
 #endif
 
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
 #include "credentials.hh"
 #include "misc.hh"
 
@@ -156,3 +160,89 @@ bool CredentialsHolder::isHashingAvailable()
   return false;
 #endif
 }
+
+#include <signal.h>
+#include <termios.h>
+
+std::string CredentialsHolder::readFromTerminal()
+{
+  struct termios term;
+  struct termios oterm;
+  memset(&term, 0, sizeof(term));
+  term.c_lflag |= ECHO;
+  memset(&oterm, 0, sizeof(oterm));
+  oterm.c_lflag |= ECHO;
+  bool restoreTermSettings = false;
+  int termAction = TCSAFLUSH;
+#ifdef TCSASOFT
+  termAction |= TCSASOFT
+#endif
+
+  FDWrapper input(open("/dev/tty", O_RDONLY));
+  if (int(input) != -1) {
+    if (tcgetattr(input, &oterm) == 0) {
+      memcpy(&term, &oterm, sizeof(term));
+      term.c_lflag &= ~(ECHO | ECHONL);
+      tcsetattr(input, termAction, &term);
+      restoreTermSettings = true;
+    }
+  }
+  else {
+    input = FDWrapper(dup(STDIN_FILENO));
+  }
+  FDWrapper output(open("/dev/tty", O_WRONLY));
+  if (int(output) == -1) {
+    output = FDWrapper(dup(STDERR_FILENO));
+  }
+
+  struct std::map<int, struct sigaction> signals;
+  struct sigaction sa;
+  sigemptyset(&sa.sa_mask);
+  sa.sa_flags = 0;
+  sa.sa_handler = [](int s) { };
+  sigaction(SIGALRM, &sa, &signals[SIGALRM]);
+  sigaction(SIGHUP, &sa, &signals[SIGHUP]);
+  sigaction(SIGINT, &sa, &signals[SIGINT]);
+  sigaction(SIGPIPE, &sa, &signals[SIGPIPE]);
+  sigaction(SIGQUIT, &sa, &signals[SIGQUIT]);
+  sigaction(SIGTERM, &sa, &signals[SIGTERM]);
+  sigaction(SIGTSTP, &sa, &signals[SIGTSTP]);
+  sigaction(SIGTTIN, &sa, &signals[SIGTTIN]);
+  sigaction(SIGTTOU, &sa, &signals[SIGTTOU]);
+
+  std::string buffer;
+  /* let's allocate a huge buffer now to prevent reallocation,
+     which would leave parts of the buffer around */
+  buffer.reserve(512);
+
+  for (;;) {
+    char ch = '\0';
+    auto got = read(input, &ch, 1);
+    if (got == 1 && ch != '\n' && ch != '\r') {
+      buffer.push_back(ch);
+    }
+    else {
+      break;
+    }
+  }
+
+  if (!(term.c_lflag & ECHO)) {
+    if (write(output, "\n", 1) != 1) {
+      /* the compiler _really_ wants the result of write() to be checked.. */
+    }
+  }
+
+  if (restoreTermSettings) {
+    tcsetattr(input, termAction, &oterm);
+  }
+
+  for (const auto& sig : signals) {
+    sigaction(sig.first, &sig.second, nullptr);
+  }
+
+#ifdef HAVE_LIBSODIUM
+  sodium_mlock(buffer.data(), buffer.size());
+#endif
+
+  return buffer;
+}
index 9d42669d6b3288f741f1b39c6e3156dd6f0a1e8a..2ed4ef938aa1980a63951108b6828fca68780068 100644 (file)
@@ -21,6 +21,7 @@
  */
 #pragma once
 
+#include <memory>
 #include <string>
 
 std::string hashPassword(const std::string& password);
@@ -51,6 +52,7 @@ public:
   }
 
   static bool isHashingAvailable();
+  static std::string readFromTerminal();
 
 private:
   std::string d_credentials;
index 9ac96d8cae4961c5d945e23b4d036a9d7e76ccee..e55782e0ea1178a44bb81b8feae32b35a41b40f2 100644 (file)
@@ -2332,7 +2332,7 @@ try
     cout<<"generate-zone-key {zsk|ksk} [ALGORITHM] [BITS]"<<endl;
     cout<<"                                   Generate a ZSK or KSK to stdout with specified ALGORITHM and BITS"<<endl;
     cout<<"get-meta ZONE [KIND ...]           Get zone metadata. If no KIND given, lists all known"<<endl;
-    cout<<"hash-password PASSWORD             Take a plaintext password or api key and output a hashed and salted version"<<endl;
+    cout<<"hash-password                      Ask for a plaintext password or api key and output a hashed and salted version"<<endl;
     cout<<"hash-zone-record ZONE RNAME        Calculate the NSEC3 hash for RNAME in ZONE"<<endl;
 #ifdef HAVE_P11KIT1
     cout<<"hsm assign ZONE ALGORITHM {ksk|zsk} MODULE SLOT PIN LABEL"<<endl<<
@@ -2484,11 +2484,8 @@ try
     return 0;
   }
   else if (cmds[0]=="hash-password") {
-    if (cmds.size() < 2) {
-      cerr<<"Syntax: pdnsutil hash-password PASSWORD"<<endl;
-      return 0;
-    }
-    cout<<hashPassword(cmds.at(1))<<endl;
+    auto password = CredentialsHolder::readFromTerminal();
+    cout<<hashPassword(password)<<endl;
     return 0;
   }
 
index 6e61171eef1eaa3b5e48b33df4969f393e745650..54451ec42662238f4f2a60e2b36da22db4b19dea 100644 (file)
@@ -142,15 +142,9 @@ int main(int argc, char** argv)
         }
       }
       else if (commands.at(i) == "hash-password") {
-        if (commands.size() > (i + 1)) {
-          ++i;
-          auto password = commands.at(i);
-          cout << hashPassword(password) << endl;
-          return 0;
-        }
-        else {
-          throw PDNSException("Command needs a password argument");
-        }
+        auto password = CredentialsHolder::readFromTerminal();
+        cout << hashPassword(password) << endl;
+        return 0;
       }
       ++i;
     }
index f690b6650c47ef4f869ad684f222244d600e9a5c..b0fb9d9d7e696d69d7e41b9dfce9aecd85463537 100644 (file)
@@ -153,9 +153,9 @@ get-qtypelist
     Retrieves QType statistics. Queries from cache aren't being counted yet.
 
 hash-password
-    Hash and salt the given password, to use as a webserver password or
-    API key. This command does not contact the recursor but does the
-    hashing inside rec_control.
+    Asks for a password then returns the hashed and salted version,
+    to use as a webserver password or API key. This command does
+    not contact the recursor but does the hashing inside rec_control.
 
 help
     Shows a list of supported commands understood by the running
index 6e16c30d12265712f0f873d4e0136253665d2a02..750a98ea7c64c5d6bf0674162b2ef325ffba6259 100644 (file)
@@ -101,7 +101,7 @@ Directory where the REST API stores its configuration and zones.
 -  String
 -  Default: unset
 
-Static pre-shared authentication key for access to the REST API. Since 4.6.0 the key can be hashed and salted using ``rec_control hash-password APIKEY`` instead of being stored in the configuration in plaintext.
+Static pre-shared authentication key for access to the REST API. Since 4.6.0 the key can be hashed and salted using ``rec_control hash-password`` instead of being stored in the configuration in plaintext.
 
 .. _setting-api-readonly:
 
@@ -2172,7 +2172,7 @@ The value between the hooks is a UUID that is generated for each request. This c
 -  String
 -  Default: unset
 
-Password required to access the webserver. Since 4.6.0 the password can be hashed and salted using ``rec_control hash-password PASS`` instead of being in plaintext.
+Password required to access the webserver. Since 4.6.0 the password can be hashed and salted using ``rec_control hash-password`` instead of being in plaintext.
 
 .. _setting-webserver-port: