blob: 50e4434be08333add1ef05e3b46c815d5c18dde3 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2014-2017, Regents of the University of California,
* Arizona Board of Regents,
* Colorado State University,
* University Pierre & Marie Curie, Sorbonne University,
* Washington University in St. Louis,
* Beijing Institute of Technology,
* The University of Memphis.
*
* This file is part of NFD (Named Data Networking Forwarding Daemon).
* See AUTHORS.md for complete list of NFD authors and contributors.
*
* NFD is free software: you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation,
* either version 3 of the License, or (at your option) any later version.
*
* NFD 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
* NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
*/
#include "command-authenticator.hpp"
#include "core/logger.hpp"
#include <ndn-cxx/security/v1/identity-certificate.hpp>
#include <ndn-cxx/security/validator-null.hpp>
#include <ndn-cxx/util/io.hpp>
#include <boost/filesystem.hpp>
namespace nfd {
NFD_LOG_INIT("CommandAuthenticator");
// INFO: configuration change, etc
// DEBUG: per authentication request result
using ndn::security::v1::IdentityCertificate;
shared_ptr<CommandAuthenticator>
CommandAuthenticator::create()
{
return shared_ptr<CommandAuthenticator>(new CommandAuthenticator());
}
CommandAuthenticator::CommandAuthenticator()
: m_validator(make_unique<ndn::ValidatorNull>())
{
}
void
CommandAuthenticator::setConfigFile(ConfigFile& configFile)
{
configFile.addSectionHandler("authorizations",
bind(&CommandAuthenticator::processConfig, this, _1, _2, _3));
}
void
CommandAuthenticator::processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename)
{
if (!isDryRun) {
NFD_LOG_INFO("clear-authorizations");
for (auto& kv : m_moduleAuth) {
kv.second.allowAny = false;
kv.second.certs.clear();
}
}
if (section.empty()) {
BOOST_THROW_EXCEPTION(ConfigFile::Error("'authorize' is missing under 'authorizations'"));
}
int authSectionIndex = 0;
for (const auto& kv : section) {
if (kv.first != "authorize") {
BOOST_THROW_EXCEPTION(ConfigFile::Error(
"'" + kv.first + "' section is not permitted under 'authorizations'"));
}
const ConfigSection& authSection = kv.second;
std::string certfile;
try {
certfile = authSection.get<std::string>("certfile");
}
catch (const boost::property_tree::ptree_error&) {
BOOST_THROW_EXCEPTION(ConfigFile::Error(
"'certfile' is missing under authorize[" + to_string(authSectionIndex) + "]"));
}
bool isAny = false;
shared_ptr<IdentityCertificate> cert;
if (certfile == "any") {
isAny = true;
NFD_LOG_WARN("'certfile any' is intended for demo purposes only and "
"SHOULD NOT be used in production environments");
}
else {
using namespace boost::filesystem;
path certfilePath = absolute(certfile, path(filename).parent_path());
cert = ndn::io::load<IdentityCertificate>(certfilePath.string());
if (cert == nullptr) {
BOOST_THROW_EXCEPTION(ConfigFile::Error(
"cannot load certfile " + certfilePath.string() +
" for authorize[" + to_string(authSectionIndex) + "]"));
}
}
const ConfigSection* privSection = nullptr;
try {
privSection = &authSection.get_child("privileges");
}
catch (const boost::property_tree::ptree_error&) {
BOOST_THROW_EXCEPTION(ConfigFile::Error(
"'privileges' is missing under authorize[" + to_string(authSectionIndex) + "]"));
}
if (privSection->empty()) {
NFD_LOG_WARN("No privileges granted to certificate " << certfile);
}
for (const auto& kv : *privSection) {
const std::string& module = kv.first;
auto found = m_moduleAuth.find(module);
if (found == m_moduleAuth.end()) {
BOOST_THROW_EXCEPTION(ConfigFile::Error(
"unknown module '" + module + "' under authorize[" + to_string(authSectionIndex) + "]"));
}
if (isDryRun) {
continue;
}
if (isAny) {
found->second.allowAny = true;
NFD_LOG_INFO("authorize module=" << module << " signer=any");
}
else {
const Name& keyName = cert->getPublicKeyName();
found->second.certs.emplace(keyName, cert->getPublicKeyInfo());
NFD_LOG_INFO("authorize module=" << module << " signer=" << keyName <<
" certfile=" << certfile);
}
}
++authSectionIndex;
}
}
ndn::mgmt::Authorization
CommandAuthenticator::makeAuthorization(const std::string& module, const std::string& verb)
{
m_moduleAuth[module]; // declares module, so that privilege is recognized
auto self = this->shared_from_this();
return [=] (const Name& prefix, const Interest& interest,
const ndn::mgmt::ControlParameters* params,
const ndn::mgmt::AcceptContinuation& accept,
const ndn::mgmt::RejectContinuation& reject) {
const AuthorizedCerts& authorized = self->m_moduleAuth.at(module);
if (authorized.allowAny) {
NFD_LOG_DEBUG("accept " << interest.getName() << " allowAny");
accept("*");
return;
}
bool isOk = false;
Name keyName;
std::tie(isOk, keyName) = CommandAuthenticator::extractKeyName(interest);
if (!isOk) {
NFD_LOG_DEBUG("reject " << interest.getName() << " bad-KeyLocator");
reject(ndn::mgmt::RejectReply::SILENT);
return;
}
auto found = authorized.certs.find(keyName);
if (found == authorized.certs.end()) {
NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " not-authorized");
reject(ndn::mgmt::RejectReply::STATUS403);
return;
}
bool hasGoodSig = ndn::Validator::verifySignature(interest, found->second);
if (!hasGoodSig) {
NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " bad-sig");
reject(ndn::mgmt::RejectReply::STATUS403);
return;
}
self->m_validator.validate(interest,
bind([=] {
NFD_LOG_DEBUG("accept " << interest.getName() << " signer=" << keyName);
accept(keyName.toUri());
}),
bind([=] {
NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " invalid-timestamp");
reject(ndn::mgmt::RejectReply::STATUS403);
}));
};
}
std::pair<bool, Name>
CommandAuthenticator::extractKeyName(const Interest& interest)
{
const Name& name = interest.getName();
if (name.size() < ndn::command_interest::MIN_SIZE) {
return {false, Name()};
}
ndn::SignatureInfo sig;
try {
sig.wireDecode(name[ndn::command_interest::POS_SIG_INFO].blockFromValue());
}
catch (const tlv::Error&) {
return {false, Name()};
}
if (!sig.hasKeyLocator()) {
return {false, Name()};
}
const ndn::KeyLocator& keyLocator = sig.getKeyLocator();
if (keyLocator.getType() != ndn::KeyLocator::KeyLocator_Name) {
return {false, Name()};
}
try {
return {true, IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName())};
}
catch (const IdentityCertificate::Error&) {
return {false, Name()};
}
}
} // namespace nfd