mgmt, main: add support for authorized commands

refs: #1227

Change-Id: I907d1fa6e78775470c5376fcdfe898be4c311001
diff --git a/daemon/mgmt/command-validator.cpp b/daemon/mgmt/command-validator.cpp
new file mode 100644
index 0000000..9028ba2
--- /dev/null
+++ b/daemon/mgmt/command-validator.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "command-validator.hpp"
+#include <ndn-cpp-dev/util/io.hpp>
+#include <ndn-cpp-dev/security/identity-certificate.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("CommandValidator");
+
+CommandValidator::CommandValidator()
+{
+
+}
+
+CommandValidator::~CommandValidator()
+{
+
+}
+
+void
+CommandValidator::setConfigFile(ConfigFile& configFile)
+{
+  configFile.addSectionHandler("authorizations",
+                               bind(&CommandValidator::onConfig, this, _1, _2));
+}
+
+static inline void
+aggregateErrors(std::stringstream& ss, const std::string& msg)
+{
+  if (!ss.str().empty())
+    {
+      ss << "\n";
+    }
+  ss << msg;
+}
+
+void
+CommandValidator::onConfig(const ConfigSection& section,
+                           bool isDryRun)
+{
+  const ConfigSection EMPTY_SECTION;
+
+  if (section.begin() == section.end())
+    {
+      throw ConfigFile::Error("No authorize sections found");
+    }
+
+  std::stringstream dryRunErrors;
+  ConfigSection::const_iterator authIt;
+  for (authIt = section.begin(); authIt != section.end(); authIt++)
+    {
+      std::string keyfile;
+      try
+        {
+          keyfile = authIt->second.get<std::string>("keyfile");
+        }
+      catch (const std::runtime_error& e)
+        {
+          std::string msg = "No keyfile specified";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      std::ifstream in;
+      in.open(keyfile.c_str());
+      if (!in.is_open())
+        {
+          std::string msg = "Unable to open key file " + keyfile;
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      shared_ptr<ndn::IdentityCertificate> id;
+      try
+        {
+          id = ndn::io::load<ndn::IdentityCertificate>(in);
+        }
+      catch(const std::runtime_error& error)
+        {
+          std::string msg = "Malformed key file " + keyfile;
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      in.close();
+
+      const ConfigSection* privileges = 0;
+
+      try
+        {
+          privileges = &authIt->second.get_child("privileges");
+        }
+      catch (const std::runtime_error& error)
+        {
+          std::string msg = "No privileges section found for key file " +
+            keyfile + " (" + id->getPublicKeyName().toUri() + ")";
+          if (!isDryRun)
+            {
+              throw ConfigFile::Error(msg);
+            }
+          aggregateErrors(dryRunErrors, msg);
+          continue;
+        }
+
+      if (privileges->begin() == privileges->end())
+        {
+          NFD_LOG_WARN("No privileges specified for key file " << keyfile + " (" << id->getPublicKeyName().toUri() << ")");
+        }
+
+      ConfigSection::const_iterator privIt;
+      for (privIt = privileges->begin(); privIt != privileges->end(); privIt++)
+        {
+          const std::string& privilegeName = privIt->first;
+          if (m_supportedPrivileges.find(privilegeName) != m_supportedPrivileges.end())
+            {
+              NFD_LOG_INFO("Giving privilege \"" << privilegeName
+                           << "\" to key " << id->getPublicKeyName());
+              if (!isDryRun)
+                {
+                  const std::string regex = "^<localhost><nfd><" + privilegeName + ">";
+                  m_validator.addInterestRule(regex, *id);
+                }
+            }
+          else
+            {
+              // Invalid configuration
+              std::string msg = "Invalid privilege \"" + privilegeName + "\" for key file " +
+                keyfile + " (" + id->getPublicKeyName().toUri() + ")";
+              if (!isDryRun)
+                {
+                  throw ConfigFile::Error(msg);
+                }
+              aggregateErrors(dryRunErrors, msg);
+            }
+        }
+    }
+
+  if (!dryRunErrors.str().empty())
+    {
+      throw ConfigFile::Error(dryRunErrors.str());
+    }
+}
+
+void
+CommandValidator::addSupportedPrivilege(const std::string& privilege)
+{
+  if (m_supportedPrivileges.find(privilege) != m_supportedPrivileges.end())
+    {
+      throw CommandValidator::Error("Duplicated privivilege: " + privilege);
+    }
+  m_supportedPrivileges.insert(privilege);
+}
+
+} // namespace nfd
+
diff --git a/daemon/mgmt/command-validator.hpp b/daemon/mgmt/command-validator.hpp
new file mode 100644
index 0000000..466bb70
--- /dev/null
+++ b/daemon/mgmt/command-validator.hpp
@@ -0,0 +1,95 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_COMMAND_VALIDATOR_HPP
+#define NFD_MGMT_COMMAND_VALIDATOR_HPP
+
+#include "config-file.hpp"
+#include <ndn-cpp-dev/util/command-interest-validator.hpp>
+
+namespace nfd {
+
+class CommandValidator
+{
+public:
+
+  class Error : public std::runtime_error
+  {
+  public:
+    Error(const std::string& what)
+      : std::runtime_error(what)
+    {
+
+    }
+  };
+
+  CommandValidator();
+
+  ~CommandValidator();
+
+  void
+  setConfigFile(ConfigFile& configFile);
+
+  /**
+   * \param section "authorizations" section to parse
+   * \param isDryRun true if performing a dry run of configuration, false otherwise
+   * \throws ConfigFile::Error on parse error
+   */
+  void
+  onConfig(const ConfigSection& section, bool isDryRun);
+
+  /**
+   * \param privilege name of privilege to add
+   * \throws CommandValidator::Error on duplicated privilege
+   */
+  void
+  addSupportedPrivilege(const std::string& privilege);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
+private:
+  ndn::CommandInterestValidator m_validator;
+  std::set<std::string> m_supportedPrivileges;
+};
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const ndn::IdentityCertificate& certificate)
+{
+  m_validator.addInterestRule(regex, certificate);
+}
+
+inline void
+CommandValidator::addInterestRule(const std::string& regex,
+                                  const Name& keyName,
+                                  const ndn::PublicKey& publicKey)
+{
+  m_validator.addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+CommandValidator::validate(const Interest& interest,
+                           const ndn::OnInterestValidated& onValidated,
+                           const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_validator.validate(interest, onValidated, onValidationFailed);
+}
+
+} // namespace nfd
+
+#endif // NFD_MGMT_COMMAND_VALIDATOR_HPP
diff --git a/daemon/mgmt/config-file.cpp b/daemon/mgmt/config-file.cpp
index a55650e..8018310 100644
--- a/daemon/mgmt/config-file.cpp
+++ b/daemon/mgmt/config-file.cpp
@@ -34,7 +34,7 @@
     {
       std::string msg = "Failed to read configuration file: ";
       msg += filename;
-      throw Error(filename);
+      throw Error(msg);
     }
   parse(inputFile, isDryRun, filename);
   inputFile.close();
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 30e3abd..9874176 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -34,7 +34,7 @@
 
 const size_t FaceManager::COMMAND_SIGNED_NCOMPS =
   FaceManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 4 (timestamp, nonce, signed info tlv, signature tlv)
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 const FaceManager::VerbAndProcessor FaceManager::COMMAND_VERBS[] =
   {
@@ -52,8 +52,8 @@
 
 
 FaceManager::FaceManager(FaceTable& faceTable,
-                         shared_ptr<AppFace> face)
-  : ManagerBase(face)
+                         shared_ptr<InternalFace> face)
+  : ManagerBase(face, FACE_MANAGER_PRIVILEGE)
   , m_faceTable(faceTable)
   , m_verbDispatch(COMMAND_VERBS,
                    COMMAND_VERBS +
@@ -411,7 +411,9 @@
       return;
     }
 
-  onValidatedFaceRequest(request.shared_from_this());
+  validate(request,
+           bind(&FaceManager::onValidatedFaceRequest, this, _1),
+           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
 }
 
 void
@@ -430,14 +432,6 @@
           return;
         }
 
-      /// \todo authorize command
-      if (false)
-        {
-          NFD_LOG_INFO("command result: unauthorized verb: " << command);
-          sendResponse(command, 403, "Unauthorized command");
-          return;
-        }
-
       NFD_LOG_INFO("command result: processing verb: " << verb);
       (verbProcessor->second)(this, command, options);
     }
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index 87574cd..316af2a 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -35,7 +35,7 @@
    */
 
   FaceManager(FaceTable& faceTable,
-              shared_ptr<AppFace> face);
+              shared_ptr<InternalFace> face);
 
   /** \brief Subscribe to a face management section(s) for the config file
    */
diff --git a/daemon/mgmt/fib-manager.cpp b/daemon/mgmt/fib-manager.cpp
index 8e27114..dee765b 100644
--- a/daemon/mgmt/fib-manager.cpp
+++ b/daemon/mgmt/fib-manager.cpp
@@ -17,18 +17,18 @@
 
 NFD_LOG_INIT("FibManager");
 
-const Name FibManager::FIB_MANAGER_COMMAND_PREFIX = "/localhost/nfd/fib";
+const Name FibManager::COMMAND_PREFIX = "/localhost/nfd/fib";
 
-const size_t FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS =
-  FibManager::FIB_MANAGER_COMMAND_PREFIX.size() +
+const size_t FibManager::COMMAND_UNSIGNED_NCOMPS =
+  FibManager::COMMAND_PREFIX.size() +
   1 + // verb
   1;  // verb options
 
-const size_t FibManager::FIB_MANAGER_COMMAND_SIGNED_NCOMPS =
-  FibManager::FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 3 (timestamp, signed info tlv, signature tlv)
+const size_t FibManager::COMMAND_SIGNED_NCOMPS =
+  FibManager::COMMAND_UNSIGNED_NCOMPS +
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
-const FibManager::VerbAndProcessor FibManager::FIB_MANAGER_COMMAND_VERBS[] =
+const FibManager::VerbAndProcessor FibManager::COMMAND_VERBS[] =
   {
     VerbAndProcessor(
                      Name::Component("insert"),
@@ -45,8 +45,6 @@
                      &FibManager::addNextHop
                      ),
 
-
-
     VerbAndProcessor(
                      Name::Component("remove-nexthop"),
                      &FibManager::removeNextHop
@@ -56,13 +54,13 @@
 
 FibManager::FibManager(Fib& fib,
                        function<shared_ptr<Face>(FaceId)> getFace,
-                       shared_ptr<AppFace> face)
-  : ManagerBase(face),
+                       shared_ptr<InternalFace> face)
+  : ManagerBase(face, FIB_PRIVILEGE),
     m_managedFib(fib),
     m_getFace(getFace),
-    m_verbDispatch(FIB_MANAGER_COMMAND_VERBS,
-                   FIB_MANAGER_COMMAND_VERBS +
-                   (sizeof(FIB_MANAGER_COMMAND_VERBS) / sizeof(VerbAndProcessor)))
+    m_verbDispatch(COMMAND_VERBS,
+                   COMMAND_VERBS +
+                   (sizeof(COMMAND_VERBS) / sizeof(VerbAndProcessor)))
 {
   face->setInterestFilter("/localhost/nfd/fib",
                           bind(&FibManager::onFibRequest, this, _2));
@@ -74,56 +72,56 @@
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
 
-
-
-  if (FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
-      commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS)
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
     {
       NFD_LOG_INFO("command result: unsigned verb: " << command);
       sendResponse(command, 401, "Signature required");
 
       return;
     }
-  else if (commandNComps < FIB_MANAGER_COMMAND_SIGNED_NCOMPS ||
-      !FIB_MANAGER_COMMAND_PREFIX.isPrefixOf(command))
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+      !COMMAND_PREFIX.isPrefixOf(command))
     {
       NFD_LOG_INFO("command result: malformed");
       sendResponse(command, 400, "Malformed command");
       return;
     }
 
-  const Name::Component& verb = command.get(FIB_MANAGER_COMMAND_PREFIX.size());
+  validate(request,
+           bind(&FibManager::onValidatedFibRequest,
+                this, _1),
+           bind(&ManagerBase::onCommandValidationFailed,
+                this, _1, _2));
+}
+
+void
+FibManager::onValidatedFibRequest(const shared_ptr<const Interest>& request)
+{
+  const Name& command = request->getName();
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size());
 
   VerbDispatchTable::const_iterator verbProcessor = m_verbDispatch.find (verb);
   if (verbProcessor != m_verbDispatch.end())
     {
       FibManagementOptions options;
-      if (!extractOptions(request, options))
+      if (!extractOptions(*request, options))
         {
+          NFD_LOG_INFO("command result: malformed verb: " << verb);
           sendResponse(command, 400, "Malformed command");
           return;
         }
 
-      /// \todo authorize command
-      if (false)
-        {
-          NFD_LOG_INFO("command result: unauthorized verb: " << command);
-          sendResponse(request.getName(), 403, "Unauthorized command");
-          return;
-        }
-
       NFD_LOG_INFO("command result: processing verb: " << verb);
       ControlResponse response;
       (verbProcessor->second)(this, options, response);
-
       sendResponse(command, response);
     }
   else
     {
       NFD_LOG_INFO("command result: unsupported verb: " << verb);
-      sendResponse(request.getName(), 501, "Unsupported command");
+      sendResponse(command, 501, "Unsupported command");
     }
-
 }
 
 bool
@@ -131,8 +129,7 @@
                            FibManagementOptions& extractedOptions)
 {
   const Name& command = request.getName();
-  const size_t optionCompIndex =
-    FIB_MANAGER_COMMAND_PREFIX.size() + 1;
+  const size_t optionCompIndex = COMMAND_PREFIX.size() + 1;
 
   try
     {
@@ -220,7 +217,7 @@
 
 void
 FibManager::removeNextHop(const FibManagementOptions& options,
-                          ControlResponse &response)
+                          ControlResponse& response)
 {
   NFD_LOG_DEBUG("remove-nexthop prefix: " << options.getName()
                 << " faceid: " << options.getFaceId());
diff --git a/daemon/mgmt/fib-manager.hpp b/daemon/mgmt/fib-manager.hpp
index c7b7793..43b410e 100644
--- a/daemon/mgmt/fib-manager.hpp
+++ b/daemon/mgmt/fib-manager.hpp
@@ -22,13 +22,15 @@
 class Forwarder;
 class Fib;
 
+const std::string FIB_PRIVILEGE = "fib"; // config file privilege name
+
 class FibManager : public ManagerBase
 {
 public:
 
   FibManager(Fib& fib,
              function<shared_ptr<Face>(FaceId)> getFace,
-             shared_ptr<AppFace> face);
+             shared_ptr<InternalFace> face);
 
   void
   onFibRequest(const Interest& request);
@@ -36,9 +38,13 @@
 private:
 
   void
+  onValidatedFibRequest(const shared_ptr<const Interest>& request);
+
+  void
   insertEntry(const FibManagementOptions& options,
               ControlResponse& response);
 
+
   void
   deleteEntry(const FibManagementOptions& options,
               ControlResponse& response);
@@ -55,9 +61,6 @@
   extractOptions(const Interest& request,
                  FibManagementOptions& extractedOptions);
 
-  // void
-  // onConfig(ConfigFile::Node section, bool isDryRun);
-
 private:
 
   Fib& m_managedFib;
@@ -75,17 +78,17 @@
 
   const VerbDispatchTable m_verbDispatch;
 
-  static const Name FIB_MANAGER_COMMAND_PREFIX; // /localhost/nfd/fib
+  static const Name COMMAND_PREFIX; // /localhost/nfd/fib
 
   // number of components in an invalid, but not malformed, unsigned command.
   // (/localhost/nfd/fib + verb + options) = 5
-  static const size_t FIB_MANAGER_COMMAND_UNSIGNED_NCOMPS;
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
 
   // number of components in a valid signed Interest.
-  // 5 in mock (see UNSIGNED_NCOMPS), 8 with signed Interest support.
-  static const size_t FIB_MANAGER_COMMAND_SIGNED_NCOMPS;
+  // UNSIGNED_NCOMPS + 4 command Interest components = 9
+  static const size_t COMMAND_SIGNED_NCOMPS;
 
-  static const VerbAndProcessor FIB_MANAGER_COMMAND_VERBS[];
+  static const VerbAndProcessor COMMAND_VERBS[];
 
 };
 
diff --git a/daemon/mgmt/internal-face.hpp b/daemon/mgmt/internal-face.hpp
index 8d5805c..3ce32e3 100644
--- a/daemon/mgmt/internal-face.hpp
+++ b/daemon/mgmt/internal-face.hpp
@@ -10,6 +10,8 @@
 #include "face/face.hpp"
 #include "app-face.hpp"
 
+#include "command-validator.hpp"
+
 namespace nfd {
 
 class InternalFace : public Face, public AppFace
@@ -25,6 +27,12 @@
 
   InternalFace();
 
+  CommandValidator&
+  getValidator();
+
+  virtual
+  ~InternalFace();
+
   // Overridden Face methods for forwarder
 
   virtual void
@@ -45,17 +53,19 @@
   virtual void
   put(const Data& data);
 
-  virtual
-  ~InternalFace();
-
 private:
 
-  // void
-  // onConfig(ConfigFile::Node section, bool isDryRun);
-
   std::map<Name, OnInterest> m_interestFilters;
+  CommandValidator m_validator;
 };
 
+inline CommandValidator&
+InternalFace::getValidator()
+{
+  return m_validator;
+}
+
+
 } // namespace nfd
 
 #endif //NFD_MGMT_INTERNAL_FACE_HPP
diff --git a/daemon/mgmt/local-control-header-manager.cpp b/daemon/mgmt/local-control-header-manager.cpp
index 5717f54..aae1d0c 100644
--- a/daemon/mgmt/local-control-header-manager.cpp
+++ b/daemon/mgmt/local-control-header-manager.cpp
@@ -6,6 +6,7 @@
 
 #include "local-control-header-manager.hpp"
 #include "face/local-face.hpp"
+#include "mgmt/internal-face.hpp"
 
 namespace nfd {
 
@@ -20,26 +21,23 @@
 
 const size_t LocalControlHeaderManager::COMMAND_SIGNED_NCOMPS =
   LocalControlHeaderManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 
 LocalControlHeaderManager::LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
-                                                     shared_ptr<AppFace> face)
-  : ManagerBase(face),
+                                                     shared_ptr<InternalFace> face)
+  : ManagerBase(face, CONTROL_HEADER_PRIVILEGE),
     m_getFace(getFace)
 {
   face->setInterestFilter("/localhost/nfd/control-header",
                           bind(&LocalControlHeaderManager::onLocalControlHeaderRequest, this, _2));
 }
 
+
+
 void
 LocalControlHeaderManager::onLocalControlHeaderRequest(const Interest& request)
 {
-  static const Name::Component MODULE_IN_FACEID("in-faceid");
-  static const Name::Component MODULE_NEXTHOP_FACEID("nexthop-faceid");
-  static const Name::Component VERB_ENABLE("enable");
-  static const Name::Component VERB_DISABLE("disable");
-
   const Name& command = request.getName();
   const size_t commandNComps = command.size();
 
@@ -59,35 +57,53 @@
       return;
     }
 
+  validate(request,
+             bind(&LocalControlHeaderManager::onCommandValidated,
+                    this, _1),
+             bind(&ManagerBase::onCommandValidationFailed,
+                    this, _1, _2));
+
+
+}
+
+void
+LocalControlHeaderManager::onCommandValidated(const shared_ptr<const Interest>& command)
+{
+  static const Name::Component MODULE_IN_FACEID("in-faceid");
+  static const Name::Component MODULE_NEXTHOP_FACEID("nexthop-faceid");
+  static const Name::Component VERB_ENABLE("enable");
+  static const Name::Component VERB_DISABLE("disable");
+
   shared_ptr<LocalFace> face =
-    dynamic_pointer_cast<LocalFace>(m_getFace(request.getIncomingFaceId()));
+    dynamic_pointer_cast<LocalFace>(m_getFace(command->getIncomingFaceId()));
 
   if (!static_cast<bool>(face))
     {
-      NFD_LOG_INFO("command result: request to enable control header on non-local face");
-      sendResponse(command, 400, "Command not supported on the requested face");
+      NFD_LOG_INFO("command result: command to enable control header on non-local face");
+      sendResponse(command->getName(), 400, "Command not supported on the requested face");
       return;
     }
 
-  const Name::Component& module = command.get(COMMAND_PREFIX.size());
-  const Name::Component& verb = command.get(COMMAND_PREFIX.size() + 1);
+  const Name& commandName = command->getName();
+  const Name::Component& module = commandName[COMMAND_PREFIX.size()];
+  const Name::Component& verb = commandName[COMMAND_PREFIX.size() + 1];
 
   if (module == MODULE_IN_FACEID)
     {
       if (verb == VERB_ENABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, true);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else if (verb == VERB_DISABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, false);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else
         {
           NFD_LOG_INFO("command result: unsupported verb: " << verb);
-          sendResponse(command, 501, "Unsupported");
+          sendResponse(commandName, 501, "Unsupported");
         }
     }
   else if (module == MODULE_NEXTHOP_FACEID)
@@ -95,23 +111,23 @@
       if (verb == VERB_ENABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, true);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else if (verb == VERB_DISABLE)
         {
           face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, false);
-          sendResponse(command, 200, "Success");
+          sendResponse(commandName, 200, "Success");
         }
       else
         {
           NFD_LOG_INFO("command result: unsupported verb: " << verb);
-          sendResponse(command, 501, "Unsupported");
+          sendResponse(commandName, 501, "Unsupported");
         }
     }
   else
     {
       NFD_LOG_INFO("command result: unsupported module: " << module);
-      sendResponse(command, 501, "Unsupported");
+      sendResponse(commandName, 501, "Unsupported");
     }
 }
 
diff --git a/daemon/mgmt/local-control-header-manager.hpp b/daemon/mgmt/local-control-header-manager.hpp
index 8c03e4e..0aa5d63 100644
--- a/daemon/mgmt/local-control-header-manager.hpp
+++ b/daemon/mgmt/local-control-header-manager.hpp
@@ -14,26 +14,31 @@
 
 namespace nfd {
 
+const std::string CONTROL_HEADER_PRIVILEGE = "control-header"; // config file privilege name
+
 class LocalControlHeaderManager : public ManagerBase
 {
 public:
   LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
-                            shared_ptr<AppFace> face);
+                            shared_ptr<InternalFace> face);
 
   void
   onLocalControlHeaderRequest(const Interest& request);
 
+  void
+  onCommandValidated(const shared_ptr<const Interest>& command);
+
 private:
   function<shared_ptr<Face>(FaceId)> m_getFace;
 
   static const Name COMMAND_PREFIX; // /localhost/nfd/control-header
 
   // number of components in an invalid, but not malformed, unsigned command.
-  // (/localhost/nfd/control-headeer + control-module + verb) = 5
+  // (/localhost/nfd/control-header + control-module + verb) = 5
   static const size_t COMMAND_UNSIGNED_NCOMPS;
 
   // number of components in a valid signed Interest.
-  // 5 in mock (see UNSIGNED_NCOMPS)
+  // UNSIGNED_NCOMPS + 4 command Interest components = 9
   static const size_t COMMAND_SIGNED_NCOMPS;
 };
 
diff --git a/daemon/mgmt/manager-base.cpp b/daemon/mgmt/manager-base.cpp
index 1994416..bd94b18 100644
--- a/daemon/mgmt/manager-base.cpp
+++ b/daemon/mgmt/manager-base.cpp
@@ -5,16 +5,15 @@
  */
 
 #include "manager-base.hpp"
-#include "mgmt/app-face.hpp"
 
 namespace nfd {
 
 NFD_LOG_INIT("ManagerBase");
 
-ManagerBase::ManagerBase(shared_ptr<AppFace> face)
+ManagerBase::ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege)
   : m_face(face)
 {
-
+  face->getValidator().addSupportedPrivilege(privilege);
 }
 
 ManagerBase::~ManagerBase()
@@ -49,5 +48,13 @@
   m_face->put(*responseData);
 }
 
+void
+ManagerBase::onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                                       const std::string& error)
+{
+  NFD_LOG_INFO("command result: unauthorized verb: " << command);
+  sendResponse(command->getName(), 403, "Unauthorized command");
+}
+
 
 } // namespace nfd
diff --git a/daemon/mgmt/manager-base.hpp b/daemon/mgmt/manager-base.hpp
index 78c6efe..9522812 100644
--- a/daemon/mgmt/manager-base.hpp
+++ b/daemon/mgmt/manager-base.hpp
@@ -10,25 +10,34 @@
 #include "common.hpp"
 #include <ndn-cpp-dev/management/nfd-control-response.hpp>
 
+#include "mgmt/command-validator.hpp"
+#include "mgmt/internal-face.hpp"
+
+
 namespace nfd {
 
 using ndn::nfd::ControlResponse;
 
-class AppFace;
+class InternalFace;
 
 class ManagerBase
 {
 public:
+
   struct Error : public std::runtime_error
   {
     Error(const std::string& what) : std::runtime_error(what) {}
   };
 
-  ManagerBase(shared_ptr<AppFace> face);
+  ManagerBase(shared_ptr<InternalFace> face, const std::string& privilege);
 
   virtual
   ~ManagerBase();
 
+  void
+  onCommandValidationFailed(const shared_ptr<const Interest>& command,
+                            const std::string& error);
+
 protected:
 
   void
@@ -50,8 +59,23 @@
                uint32_t code,
                const std::string& text);
 
+PUBLIC_WITH_TESTS_ELSE_PROTECTED:
+  void
+  addInterestRule(const std::string& regex,
+                  const ndn::IdentityCertificate& certificate);
+
+  void
+  addInterestRule(const std::string& regex,
+                  const Name& keyName,
+                  const ndn::PublicKey& publicKey);
+
+  void
+  validate(const Interest& interest,
+           const ndn::OnInterestValidated& onValidated,
+           const ndn::OnInterestValidationFailed& onValidationFailed);
+
 protected:
-  shared_ptr<AppFace> m_face;
+  shared_ptr<InternalFace> m_face;
 };
 
 inline void
@@ -73,6 +97,29 @@
   response.setBody(body);
 }
 
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const ndn::IdentityCertificate& certificate)
+{
+  m_face->getValidator().addInterestRule(regex, certificate);
+}
+
+inline void
+ManagerBase::addInterestRule(const std::string& regex,
+                             const Name& keyName,
+                             const ndn::PublicKey& publicKey)
+{
+  m_face->getValidator().addInterestRule(regex, keyName, publicKey);
+}
+
+inline void
+ManagerBase::validate(const Interest& interest,
+                      const ndn::OnInterestValidated& onValidated,
+                      const ndn::OnInterestValidationFailed& onValidationFailed)
+{
+  m_face->getValidator().validate(interest, onValidated, onValidationFailed);
+}
+
 
 } // namespace nfd
 
diff --git a/daemon/mgmt/strategy-choice-manager.cpp b/daemon/mgmt/strategy-choice-manager.cpp
index 00761dd..665f4a8 100644
--- a/daemon/mgmt/strategy-choice-manager.cpp
+++ b/daemon/mgmt/strategy-choice-manager.cpp
@@ -21,11 +21,11 @@
 
 const size_t StrategyChoiceManager::COMMAND_SIGNED_NCOMPS =
   StrategyChoiceManager::COMMAND_UNSIGNED_NCOMPS +
-  0; // No signed Interest support in mock, otherwise 4 (timestamp, nonce, signed info tlv, signature tlv)
+  4; // (timestamp, nonce, signed info tlv, signature tlv)
 
 StrategyChoiceManager::StrategyChoiceManager(StrategyChoice& strategyChoice,
-                                             shared_ptr<AppFace> face)
-  : ManagerBase(face)
+                                             shared_ptr<InternalFace> face)
+  : ManagerBase(face, STRATEGY_CHOICE_PRIVILEGE)
   , m_strategyChoice(strategyChoice)
 {
   face->setInterestFilter("/localhost/nfd/strategy-choice",
@@ -59,7 +59,9 @@
       return;
     }
 
-  onValidatedStrategyChoiceRequest(request.shared_from_this());
+  validate(request,
+           bind(&StrategyChoiceManager::onValidatedStrategyChoiceRequest, this, _1),
+           bind(&ManagerBase::onCommandValidationFailed, this, _1, _2));
 }
 
 void
diff --git a/daemon/mgmt/strategy-choice-manager.hpp b/daemon/mgmt/strategy-choice-manager.hpp
index 34046ac..6313fbb 100644
--- a/daemon/mgmt/strategy-choice-manager.hpp
+++ b/daemon/mgmt/strategy-choice-manager.hpp
@@ -21,7 +21,7 @@
 {
 public:
   StrategyChoiceManager(StrategyChoice& strategyChoice,
-                        shared_ptr<AppFace> face);
+                        shared_ptr<InternalFace> face);
 
   virtual
   ~StrategyChoiceManager();