blob: 50e4434be08333add1ef05e3b46c815d5c18dde3 [file] [log] [blame]
Junxiao Shid7631272016-08-17 04:16:31 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev635bf202017-03-09 21:57:34 +00003 * Copyright (c) 2014-2017, Regents of the University of California,
Junxiao Shid7631272016-08-17 04:16:31 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "command-authenticator.hpp"
27#include "core/logger.hpp"
28
Alexander Afanasyev635bf202017-03-09 21:57:34 +000029#include <ndn-cxx/security/v1/identity-certificate.hpp>
Junxiao Shid7631272016-08-17 04:16:31 +000030#include <ndn-cxx/security/validator-null.hpp>
31#include <ndn-cxx/util/io.hpp>
32
33#include <boost/filesystem.hpp>
34
35namespace nfd {
36
37NFD_LOG_INIT("CommandAuthenticator");
38// INFO: configuration change, etc
39// DEBUG: per authentication request result
40
Alexander Afanasyev635bf202017-03-09 21:57:34 +000041using ndn::security::v1::IdentityCertificate;
42
Junxiao Shid7631272016-08-17 04:16:31 +000043shared_ptr<CommandAuthenticator>
44CommandAuthenticator::create()
45{
46 return shared_ptr<CommandAuthenticator>(new CommandAuthenticator());
47}
48
49CommandAuthenticator::CommandAuthenticator()
50 : m_validator(make_unique<ndn::ValidatorNull>())
51{
52}
53
54void
55CommandAuthenticator::setConfigFile(ConfigFile& configFile)
56{
57 configFile.addSectionHandler("authorizations",
58 bind(&CommandAuthenticator::processConfig, this, _1, _2, _3));
59}
60
61void
62CommandAuthenticator::processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename)
63{
64 if (!isDryRun) {
65 NFD_LOG_INFO("clear-authorizations");
66 for (auto& kv : m_moduleAuth) {
67 kv.second.allowAny = false;
68 kv.second.certs.clear();
69 }
70 }
71
72 if (section.empty()) {
73 BOOST_THROW_EXCEPTION(ConfigFile::Error("'authorize' is missing under 'authorizations'"));
74 }
75
76 int authSectionIndex = 0;
77 for (const auto& kv : section) {
78 if (kv.first != "authorize") {
79 BOOST_THROW_EXCEPTION(ConfigFile::Error(
80 "'" + kv.first + "' section is not permitted under 'authorizations'"));
81 }
82 const ConfigSection& authSection = kv.second;
83
84 std::string certfile;
85 try {
86 certfile = authSection.get<std::string>("certfile");
87 }
88 catch (const boost::property_tree::ptree_error&) {
89 BOOST_THROW_EXCEPTION(ConfigFile::Error(
90 "'certfile' is missing under authorize[" + to_string(authSectionIndex) + "]"));
91 }
92
93 bool isAny = false;
Alexander Afanasyev635bf202017-03-09 21:57:34 +000094 shared_ptr<IdentityCertificate> cert;
Junxiao Shid7631272016-08-17 04:16:31 +000095 if (certfile == "any") {
96 isAny = true;
97 NFD_LOG_WARN("'certfile any' is intended for demo purposes only and "
98 "SHOULD NOT be used in production environments");
99 }
100 else {
101 using namespace boost::filesystem;
102 path certfilePath = absolute(certfile, path(filename).parent_path());
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000103 cert = ndn::io::load<IdentityCertificate>(certfilePath.string());
Junxiao Shid7631272016-08-17 04:16:31 +0000104 if (cert == nullptr) {
105 BOOST_THROW_EXCEPTION(ConfigFile::Error(
106 "cannot load certfile " + certfilePath.string() +
107 " for authorize[" + to_string(authSectionIndex) + "]"));
108 }
109 }
110
111 const ConfigSection* privSection = nullptr;
112 try {
113 privSection = &authSection.get_child("privileges");
114 }
115 catch (const boost::property_tree::ptree_error&) {
116 BOOST_THROW_EXCEPTION(ConfigFile::Error(
117 "'privileges' is missing under authorize[" + to_string(authSectionIndex) + "]"));
118 }
119
120 if (privSection->empty()) {
121 NFD_LOG_WARN("No privileges granted to certificate " << certfile);
122 }
123 for (const auto& kv : *privSection) {
124 const std::string& module = kv.first;
125 auto found = m_moduleAuth.find(module);
126 if (found == m_moduleAuth.end()) {
127 BOOST_THROW_EXCEPTION(ConfigFile::Error(
128 "unknown module '" + module + "' under authorize[" + to_string(authSectionIndex) + "]"));
129 }
130
131 if (isDryRun) {
132 continue;
133 }
134
135 if (isAny) {
136 found->second.allowAny = true;
137 NFD_LOG_INFO("authorize module=" << module << " signer=any");
138 }
139 else {
140 const Name& keyName = cert->getPublicKeyName();
141 found->second.certs.emplace(keyName, cert->getPublicKeyInfo());
142 NFD_LOG_INFO("authorize module=" << module << " signer=" << keyName <<
143 " certfile=" << certfile);
144 }
145 }
146
147 ++authSectionIndex;
148 }
149}
150
151ndn::mgmt::Authorization
152CommandAuthenticator::makeAuthorization(const std::string& module, const std::string& verb)
153{
154 m_moduleAuth[module]; // declares module, so that privilege is recognized
155
156 auto self = this->shared_from_this();
157 return [=] (const Name& prefix, const Interest& interest,
158 const ndn::mgmt::ControlParameters* params,
159 const ndn::mgmt::AcceptContinuation& accept,
160 const ndn::mgmt::RejectContinuation& reject) {
161 const AuthorizedCerts& authorized = self->m_moduleAuth.at(module);
162 if (authorized.allowAny) {
163 NFD_LOG_DEBUG("accept " << interest.getName() << " allowAny");
164 accept("*");
165 return;
166 }
167
168 bool isOk = false;
169 Name keyName;
170 std::tie(isOk, keyName) = CommandAuthenticator::extractKeyName(interest);
171 if (!isOk) {
172 NFD_LOG_DEBUG("reject " << interest.getName() << " bad-KeyLocator");
173 reject(ndn::mgmt::RejectReply::SILENT);
174 return;
175 }
176
177 auto found = authorized.certs.find(keyName);
178 if (found == authorized.certs.end()) {
179 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " not-authorized");
180 reject(ndn::mgmt::RejectReply::STATUS403);
181 return;
182 }
183
184 bool hasGoodSig = ndn::Validator::verifySignature(interest, found->second);
185 if (!hasGoodSig) {
186 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " bad-sig");
187 reject(ndn::mgmt::RejectReply::STATUS403);
188 return;
189 }
190
191 self->m_validator.validate(interest,
192 bind([=] {
193 NFD_LOG_DEBUG("accept " << interest.getName() << " signer=" << keyName);
194 accept(keyName.toUri());
195 }),
196 bind([=] {
197 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " invalid-timestamp");
198 reject(ndn::mgmt::RejectReply::STATUS403);
199 }));
200 };
201}
202
203std::pair<bool, Name>
204CommandAuthenticator::extractKeyName(const Interest& interest)
205{
206 const Name& name = interest.getName();
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000207 if (name.size() < ndn::command_interest::MIN_SIZE) {
Junxiao Shid7631272016-08-17 04:16:31 +0000208 return {false, Name()};
209 }
210
211 ndn::SignatureInfo sig;
212 try {
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000213 sig.wireDecode(name[ndn::command_interest::POS_SIG_INFO].blockFromValue());
Junxiao Shid7631272016-08-17 04:16:31 +0000214 }
215 catch (const tlv::Error&) {
216 return {false, Name()};
217 }
218
219 if (!sig.hasKeyLocator()) {
220 return {false, Name()};
221 }
222
223 const ndn::KeyLocator& keyLocator = sig.getKeyLocator();
224 if (keyLocator.getType() != ndn::KeyLocator::KeyLocator_Name) {
225 return {false, Name()};
226 }
227
228 try {
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000229 return {true, IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName())};
Junxiao Shid7631272016-08-17 04:16:31 +0000230 }
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000231 catch (const IdentityCertificate::Error&) {
Junxiao Shid7631272016-08-17 04:16:31 +0000232 return {false, Name()};
233 }
234}
235
236} // namespace nfd