blob: cd45b841f512a22a8edce38bbecde2dd39b0a632 [file] [log] [blame]
Junxiao Shid7631272016-08-17 04:16:31 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2016, Regents of the University of California,
4 * 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
29#include <ndn-cxx/security/identity-certificate.hpp>
30#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
41shared_ptr<CommandAuthenticator>
42CommandAuthenticator::create()
43{
44 return shared_ptr<CommandAuthenticator>(new CommandAuthenticator());
45}
46
47CommandAuthenticator::CommandAuthenticator()
48 : m_validator(make_unique<ndn::ValidatorNull>())
49{
50}
51
52void
53CommandAuthenticator::setConfigFile(ConfigFile& configFile)
54{
55 configFile.addSectionHandler("authorizations",
56 bind(&CommandAuthenticator::processConfig, this, _1, _2, _3));
57}
58
59void
60CommandAuthenticator::processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename)
61{
62 if (!isDryRun) {
63 NFD_LOG_INFO("clear-authorizations");
64 for (auto& kv : m_moduleAuth) {
65 kv.second.allowAny = false;
66 kv.second.certs.clear();
67 }
68 }
69
70 if (section.empty()) {
71 BOOST_THROW_EXCEPTION(ConfigFile::Error("'authorize' is missing under 'authorizations'"));
72 }
73
74 int authSectionIndex = 0;
75 for (const auto& kv : section) {
76 if (kv.first != "authorize") {
77 BOOST_THROW_EXCEPTION(ConfigFile::Error(
78 "'" + kv.first + "' section is not permitted under 'authorizations'"));
79 }
80 const ConfigSection& authSection = kv.second;
81
82 std::string certfile;
83 try {
84 certfile = authSection.get<std::string>("certfile");
85 }
86 catch (const boost::property_tree::ptree_error&) {
87 BOOST_THROW_EXCEPTION(ConfigFile::Error(
88 "'certfile' is missing under authorize[" + to_string(authSectionIndex) + "]"));
89 }
90
91 bool isAny = false;
92 shared_ptr<ndn::IdentityCertificate> cert;
93 if (certfile == "any") {
94 isAny = true;
95 NFD_LOG_WARN("'certfile any' is intended for demo purposes only and "
96 "SHOULD NOT be used in production environments");
97 }
98 else {
99 using namespace boost::filesystem;
100 path certfilePath = absolute(certfile, path(filename).parent_path());
101 cert = ndn::io::load<ndn::IdentityCertificate>(certfilePath.string());
102 if (cert == nullptr) {
103 BOOST_THROW_EXCEPTION(ConfigFile::Error(
104 "cannot load certfile " + certfilePath.string() +
105 " for authorize[" + to_string(authSectionIndex) + "]"));
106 }
107 }
108
109 const ConfigSection* privSection = nullptr;
110 try {
111 privSection = &authSection.get_child("privileges");
112 }
113 catch (const boost::property_tree::ptree_error&) {
114 BOOST_THROW_EXCEPTION(ConfigFile::Error(
115 "'privileges' is missing under authorize[" + to_string(authSectionIndex) + "]"));
116 }
117
118 if (privSection->empty()) {
119 NFD_LOG_WARN("No privileges granted to certificate " << certfile);
120 }
121 for (const auto& kv : *privSection) {
122 const std::string& module = kv.first;
123 auto found = m_moduleAuth.find(module);
124 if (found == m_moduleAuth.end()) {
125 BOOST_THROW_EXCEPTION(ConfigFile::Error(
126 "unknown module '" + module + "' under authorize[" + to_string(authSectionIndex) + "]"));
127 }
128
129 if (isDryRun) {
130 continue;
131 }
132
133 if (isAny) {
134 found->second.allowAny = true;
135 NFD_LOG_INFO("authorize module=" << module << " signer=any");
136 }
137 else {
138 const Name& keyName = cert->getPublicKeyName();
139 found->second.certs.emplace(keyName, cert->getPublicKeyInfo());
140 NFD_LOG_INFO("authorize module=" << module << " signer=" << keyName <<
141 " certfile=" << certfile);
142 }
143 }
144
145 ++authSectionIndex;
146 }
147}
148
149ndn::mgmt::Authorization
150CommandAuthenticator::makeAuthorization(const std::string& module, const std::string& verb)
151{
152 m_moduleAuth[module]; // declares module, so that privilege is recognized
153
154 auto self = this->shared_from_this();
155 return [=] (const Name& prefix, const Interest& interest,
156 const ndn::mgmt::ControlParameters* params,
157 const ndn::mgmt::AcceptContinuation& accept,
158 const ndn::mgmt::RejectContinuation& reject) {
159 const AuthorizedCerts& authorized = self->m_moduleAuth.at(module);
160 if (authorized.allowAny) {
161 NFD_LOG_DEBUG("accept " << interest.getName() << " allowAny");
162 accept("*");
163 return;
164 }
165
166 bool isOk = false;
167 Name keyName;
168 std::tie(isOk, keyName) = CommandAuthenticator::extractKeyName(interest);
169 if (!isOk) {
170 NFD_LOG_DEBUG("reject " << interest.getName() << " bad-KeyLocator");
171 reject(ndn::mgmt::RejectReply::SILENT);
172 return;
173 }
174
175 auto found = authorized.certs.find(keyName);
176 if (found == authorized.certs.end()) {
177 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " not-authorized");
178 reject(ndn::mgmt::RejectReply::STATUS403);
179 return;
180 }
181
182 bool hasGoodSig = ndn::Validator::verifySignature(interest, found->second);
183 if (!hasGoodSig) {
184 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " bad-sig");
185 reject(ndn::mgmt::RejectReply::STATUS403);
186 return;
187 }
188
189 self->m_validator.validate(interest,
190 bind([=] {
191 NFD_LOG_DEBUG("accept " << interest.getName() << " signer=" << keyName);
192 accept(keyName.toUri());
193 }),
194 bind([=] {
195 NFD_LOG_DEBUG("reject " << interest.getName() << " signer=" << keyName << " invalid-timestamp");
196 reject(ndn::mgmt::RejectReply::STATUS403);
197 }));
198 };
199}
200
201std::pair<bool, Name>
202CommandAuthenticator::extractKeyName(const Interest& interest)
203{
204 const Name& name = interest.getName();
205 if (name.size() < ndn::signed_interest::MIN_LENGTH) {
206 return {false, Name()};
207 }
208
209 ndn::SignatureInfo sig;
210 try {
211 sig.wireDecode(name[ndn::signed_interest::POS_SIG_INFO].blockFromValue());
212 }
213 catch (const tlv::Error&) {
214 return {false, Name()};
215 }
216
217 if (!sig.hasKeyLocator()) {
218 return {false, Name()};
219 }
220
221 const ndn::KeyLocator& keyLocator = sig.getKeyLocator();
222 if (keyLocator.getType() != ndn::KeyLocator::KeyLocator_Name) {
223 return {false, Name()};
224 }
225
226 try {
227 return {true, ndn::IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName())};
228 }
229 catch (const ndn::IdentityCertificate::Error&) {
230 return {false, Name()};
231 }
232}
233
234} // namespace nfd