security: Support wildcard in NFD configuration

Change-Id: Ie27abd2315be0adf2e813049208e9cb96d16aff5
Refs: #1559
diff --git a/daemon/mgmt/command-validator.cpp b/daemon/mgmt/command-validator.cpp
index f7b0df2..ea596b1 100644
--- a/daemon/mgmt/command-validator.cpp
+++ b/daemon/mgmt/command-validator.cpp
@@ -96,46 +96,58 @@
           continue;
         }
 
-      path certfilePath = absolute(certfile, path(filename).parent_path());
-      NFD_LOG_DEBUG("generated certfile path: " << certfilePath.native());
-
-      std::ifstream in;
-      in.open(certfilePath.c_str());
-      if (!in.is_open())
-        {
-          std::string msg = "Unable to open certificate file " + certfilePath.native();
-          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)
-        {
-          // do nothing
-        }
 
-      if (!static_cast<bool>(id)) {
-        std::string msg = "Malformed certificate file " + certfilePath.native();
-        if (!isDryRun)
-          {
-            throw ConfigFile::Error(msg);
+      if (certfile != "any")
+        {
+          path certfilePath = absolute(certfile, path(filename).parent_path());
+          NFD_LOG_DEBUG("generated certfile path: " << certfilePath.native());
+
+          std::ifstream in;
+          in.open(certfilePath.c_str());
+          if (!in.is_open())
+            {
+              std::string msg = "Unable to open certificate file " + certfilePath.native();
+              if (!isDryRun)
+                {
+                  throw ConfigFile::Error(msg);
+                }
+              aggregateErrors(dryRunErrors, msg);
+              continue;
+            }
+
+          try
+            {
+              id = ndn::io::load<ndn::IdentityCertificate>(in);
+            }
+          catch (const std::runtime_error& error)
+            {
+              // do nothing
+            }
+
+          if (!static_cast<bool>(id)) {
+            std::string msg = "Malformed certificate file " + certfilePath.native();
+            if (!isDryRun)
+              {
+                throw ConfigFile::Error(msg);
+              }
+            aggregateErrors(dryRunErrors, msg);
+            continue;
           }
-        aggregateErrors(dryRunErrors, msg);
-        continue;
-      }
 
-      in.close();
+          in.close();
+        }
 
+      std::string keyNameForLogging;
+      if (static_cast<bool>(id))
+        keyNameForLogging = id->getPublicKeyName().toUri();
+      else
+        {
+          keyNameForLogging = "wildcard";
+          NFD_LOG_WARN("Wildcard identity is intended for demo purpose only and " <<
+                       "SHOULD NOT be used in production environment");
+        }
       const ConfigSection* privileges = 0;
-
       try
         {
           privileges = &authIt->second.get_child("privileges");
@@ -143,7 +155,7 @@
       catch (const std::runtime_error& error)
         {
           std::string msg = "No privileges section found for certificate file " +
-            certfile + " (" + id->getPublicKeyName().toUri() + ")";
+            certfile + " (" + keyNameForLogging + ")";
           if (!isDryRun)
             {
               throw ConfigFile::Error(msg);
@@ -155,7 +167,7 @@
       if (privileges->begin() == privileges->end())
         {
           NFD_LOG_WARN("No privileges specified for certificate file " << certfile
-                       << " (" << id->getPublicKeyName().toUri() << ")");
+                       << " (" << keyNameForLogging << ")");
         }
 
       ConfigSection::const_iterator privIt;
@@ -165,18 +177,21 @@
           if (m_supportedPrivileges.find(privilegeName) != m_supportedPrivileges.end())
             {
               NFD_LOG_INFO("Giving privilege \"" << privilegeName
-                           << "\" to identity " << id->getPublicKeyName());
+                           << "\" to identity " << keyNameForLogging);
               if (!isDryRun)
                 {
                   const std::string regex = "^<localhost><nfd><" + privilegeName + ">";
-                  m_validator.addInterestRule(regex, *id);
+                  if (static_cast<bool>(id))
+                    m_validator.addInterestRule(regex, *id);
+                  else
+                    m_validator.addInterestBypassRule(regex);
                 }
             }
           else
             {
               // Invalid configuration
-              std::string msg = "Invalid privilege \"" + privilegeName + "\" for certificate file " +
-                certfile + " (" + id->getPublicKeyName().toUri() + ")";
+              std::string msg = "Invalid privilege \"" + privilegeName +
+                "\" for certificate file " + certfile + " (" + keyNameForLogging + ")";
               if (!isDryRun)
                 {
                   throw ConfigFile::Error(msg);
diff --git a/nfd.conf.sample.in b/nfd.conf.sample.in
index 74d4333..7e18358 100644
--- a/nfd.conf.sample.in
+++ b/nfd.conf.sample.in
@@ -139,7 +139,9 @@
     ; your machine. You may move your newly created key to the location it
     ; specifies or path.
 
-    certfile keys/default.ndncert ; NDN identity certificate file
+    ; certfile keys/default.ndncert ; NDN identity certificate file
+    certfile any ; "any" authorizes command interests signed under any certificate,
+                 ; i.e., no actual validation.
     privileges ; set of privileges granted to this identity
     {
       faces
diff --git a/tests/daemon/mgmt/command-validator.cpp b/tests/daemon/mgmt/command-validator.cpp
index 09fd6b9..834cc5b 100644
--- a/tests/daemon/mgmt/command-validator.cpp
+++ b/tests/daemon/mgmt/command-validator.cpp
@@ -593,6 +593,66 @@
                         bind(&validateErrorMessage, error.str(), _1));
 }
 
+BOOST_FIXTURE_TEST_CASE(Wildcard, TwoValidatorFixture)
+{
+  const std::string WILDCARD_CERT_CONFIG =
+    "authorizations\n"
+    "{\n"
+    "  authorize\n"
+    "  {\n"
+    "    certfile any\n"
+    "    privileges\n"
+    "    {\n"
+    "      faces\n"
+    "      stats\n"
+    "    }\n"
+    "  }\n"
+    "}\n";
+
+  shared_ptr<Interest> fibCommand = make_shared<Interest>("/localhost/nfd/fib/insert");
+  shared_ptr<Interest> statsCommand = make_shared<Interest>("/localhost/nfd/stats/dosomething");
+  shared_ptr<Interest> facesCommand = make_shared<Interest>("/localhost/nfd/faces/create");
+
+  ndn::CommandInterestGenerator generator;
+  generator.generateWithIdentity(*fibCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*statsCommand, m_tester1.getIdentityName());
+  generator.generateWithIdentity(*facesCommand, m_tester1.getIdentityName());
+
+  ConfigFile config;
+  CommandValidator validator;
+  validator.addSupportedPrivilege("faces");
+  validator.addSupportedPrivilege("fib");
+  validator.addSupportedPrivilege("stats");
+
+  validator.setConfigFile(config);
+
+  config.parse(WILDCARD_CERT_CONFIG, false, CONFIG_PATH.native());
+
+  validator.validate(*fibCommand,
+                     bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+                     bind(&CommandValidatorTester::onValidationFailed,
+                          boost::ref(m_tester1), _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidationFailed());
+  m_tester1.resetValidation();
+
+  validator.validate(*statsCommand,
+                     bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+                     bind(&CommandValidatorTester::onValidationFailed,
+                          boost::ref(m_tester1), _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+  m_tester1.resetValidation();
+
+  validator.validate(*facesCommand,
+                     bind(&CommandValidatorTester::onValidated, boost::ref(m_tester1), _1),
+                     bind(&CommandValidatorTester::onValidationFailed,
+                          boost::ref(m_tester1), _1, _2));
+
+  BOOST_REQUIRE(m_tester1.commandValidated());
+  m_tester1.resetValidation();
+}
+
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace tests