NS-3 based Named Data Networking (NDN) simulator
ndnSIM 2.5: NDN, CCN, CCNx, content centric networks
API Documentation
validation-policy-config.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2021 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
24 #include "ndn-cxx/util/io.hpp"
25 
26 #include <boost/algorithm/string/predicate.hpp>
27 #include <boost/filesystem/operations.hpp>
28 #include <boost/filesystem/path.hpp>
29 #include <boost/lexical_cast.hpp>
30 #include <boost/property_tree/info_parser.hpp>
31 
32 #include <fstream>
33 
34 namespace ndn {
35 namespace security {
36 inline namespace v2 {
37 namespace validator_config {
38 
39 void
40 ValidationPolicyConfig::load(const std::string& filename)
41 {
42  std::ifstream inputFile(filename);
43  if (!inputFile) {
44  NDN_THROW(Error("Failed to read configuration file: " + filename));
45  }
46  load(inputFile, filename);
47 }
48 
49 void
50 ValidationPolicyConfig::load(const std::string& input, const std::string& filename)
51 {
52  std::istringstream inputStream(input);
53  load(inputStream, filename);
54 }
55 
56 void
57 ValidationPolicyConfig::load(std::istream& input, const std::string& filename)
58 {
59  ConfigSection tree;
60  try {
61  boost::property_tree::read_info(input, tree);
62  }
63  catch (const boost::property_tree::info_parser_error& e) {
64  NDN_THROW(Error("Failed to parse configuration file " + filename +
65  " line " + to_string(e.line()) + ": " + e.message()));
66  }
67  load(tree, filename);
68 }
69 
70 void
71 ValidationPolicyConfig::load(const ConfigSection& configSection, const std::string& filename)
72 {
73  BOOST_ASSERT(!filename.empty());
74 
75  if (m_validator == nullptr) {
76  NDN_THROW(Error("Validator instance not assigned on the policy"));
77  }
78  if (m_isConfigured) {
79  m_shouldBypass = false;
80  m_dataRules.clear();
81  m_interestRules.clear();
82  m_validator->resetAnchors();
83  m_validator->resetVerifiedCertificates();
84  }
85  m_isConfigured = true;
86 
87  for (const auto& subSection : configSection) {
88  const std::string& sectionName = subSection.first;
89  const ConfigSection& section = subSection.second;
90 
91  if (boost::iequals(sectionName, "rule")) {
92  auto rule = Rule::create(section, filename);
93  if (rule->getPktType() == tlv::Data) {
94  m_dataRules.push_back(std::move(rule));
95  }
96  else if (rule->getPktType() == tlv::Interest) {
97  m_interestRules.push_back(std::move(rule));
98  }
99  }
100  else if (boost::iequals(sectionName, "trust-anchor")) {
101  processConfigTrustAnchor(section, filename);
102  }
103  else {
104  NDN_THROW(Error("Error processing configuration file " + filename +
105  ": unrecognized section " + sectionName));
106  }
107  }
108 }
109 
110 void
111 ValidationPolicyConfig::processConfigTrustAnchor(const ConfigSection& configSection,
112  const std::string& filename)
113 {
114  using namespace boost::filesystem;
115 
116  auto propertyIt = configSection.begin();
117 
118  // Get trust-anchor.type
119  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
120  NDN_THROW(Error("Expecting <trust-anchor.type>"));
121  }
122 
123  std::string type = propertyIt->second.data();
124  propertyIt++;
125 
126  if (boost::iequals(type, "file")) {
127  // Get trust-anchor.file
128  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name")) {
129  NDN_THROW(Error("Expecting <trust-anchor.file-name>"));
130  }
131 
132  std::string file = propertyIt->second.data();
133  propertyIt++;
134 
135  time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
136  if (propertyIt != configSection.end())
137  NDN_THROW(Error("Expecting end of <trust-anchor>"));
138 
139  m_validator->loadAnchor(file, absolute(file, path(filename).parent_path()).string(),
140  refresh, false);
141  }
142  else if (boost::iequals(type, "base64")) {
143  // Get trust-anchor.base64-string
144  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
145  NDN_THROW(Error("Expecting <trust-anchor.base64-string>"));
146 
147  std::stringstream ss(propertyIt->second.data());
148  propertyIt++;
149 
150  if (propertyIt != configSection.end())
151  NDN_THROW(Error("Expecting end of <trust-anchor>"));
152 
153  auto idCert = io::load<Certificate>(ss);
154  if (idCert != nullptr) {
155  m_validator->loadAnchor("", std::move(*idCert));
156  }
157  else {
158  NDN_THROW(Error("Cannot decode certificate from base64-string"));
159  }
160  }
161  else if (boost::iequals(type, "dir")) {
162  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
163  NDN_THROW(Error("Expecting <trust-anchor.dir>"));
164 
165  std::string dirString(propertyIt->second.data());
166  propertyIt++;
167 
168  time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
169  if (propertyIt != configSection.end())
170  NDN_THROW(Error("Expecting end of <trust-anchor>"));
171 
172  path dirPath = absolute(dirString, path(filename).parent_path());
173  m_validator->loadAnchor(dirString, dirPath.string(), refresh, true);
174  }
175  else if (boost::iequals(type, "any")) {
176  m_shouldBypass = true;
177  }
178  else {
179  NDN_THROW(Error("Unrecognized <trust-anchor.type>: " + type));
180  }
181 }
182 
184 ValidationPolicyConfig::getRefreshPeriod(ConfigSection::const_iterator& it,
185  const ConfigSection::const_iterator& end)
186 {
187  auto refresh = time::nanoseconds::max();
188  if (it == end) {
189  return refresh;
190  }
191 
192  if (!boost::iequals(it->first, "refresh")) {
193  NDN_THROW(Error("Expecting <trust-anchor.refresh>"));
194  }
195 
196  std::string inputString = it->second.data();
197  ++it;
198  char unit = inputString[inputString.size() - 1];
199  std::string refreshString = inputString.substr(0, inputString.size() - 1);
200 
201  int32_t refreshPeriod = -1;
202  try {
203  refreshPeriod = boost::lexical_cast<int32_t>(refreshString);
204  }
205  catch (const boost::bad_lexical_cast&) {
206  // pass
207  }
208  if (refreshPeriod < 0) {
209  NDN_THROW(Error("Bad refresh value: " + refreshString));
210  }
211 
212  if (refreshPeriod == 0) {
213  return getDefaultRefreshPeriod();
214  }
215 
216  switch (unit) {
217  case 'h':
218  return time::hours(refreshPeriod);
219  case 'm':
220  return time::minutes(refreshPeriod);
221  case 's':
222  return time::seconds(refreshPeriod);
223  default:
224  NDN_THROW(Error("Bad refresh time unit: "s + unit));
225  }
226 }
227 
229 ValidationPolicyConfig::getDefaultRefreshPeriod()
230 {
231  return 1_h;
232 }
233 
234 void
235 ValidationPolicyConfig::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
236  const ValidationContinuation& continueValidation)
237 {
238  BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
239 
240  if (m_shouldBypass) {
241  return continueValidation(nullptr, state);
242  }
243 
244  Name klName = getKeyLocatorName(data, *state);
245  if (!state->getOutcome()) { // already failed
246  return;
247  }
248 
249  for (const auto& rule : m_dataRules) {
250  if (rule->match(tlv::Data, data.getName(), state)) {
251  if (rule->check(tlv::Data, tlv::SignatureTypeValue(data.getSignatureType()),
252  data.getName(), klName, state)) {
253  return continueValidation(make_shared<CertificateRequest>(klName), state);
254  }
255  // rule->check calls state->fail(...) if the check fails
256  return;
257  }
258  }
259 
260  return state->fail({ValidationError::POLICY_ERROR,
261  "No rule matched for data `" + data.getName().toUri() + "`"});
262 }
263 
264 void
265 ValidationPolicyConfig::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
266  const ValidationContinuation& continueValidation)
267 {
268  BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
269 
270  if (m_shouldBypass) {
271  return continueValidation(nullptr, state);
272  }
273 
274  Name klName = getKeyLocatorName(interest, *state);
275  if (!state->getOutcome()) { // already failed
276  return;
277  }
278 
279  for (const auto& rule : m_interestRules) {
280  if (rule->match(tlv::Interest, interest.getName(), state)) {
281 
282  tlv::SignatureTypeValue sigType;
283  auto fmt = state->getTag<SignedInterestFormatTag>();
284  BOOST_ASSERT(fmt);
285 
286  if (*fmt == SignedInterestFormat::V03) {
287  sigType = tlv::SignatureTypeValue(interest.getSignatureInfo()->getSignatureType());
288  }
289  else {
290  if (interest.getName().size() < signed_interest::MIN_SIZE) {
291  state->fail({ValidationError::INVALID_KEY_LOCATOR, "Invalid signed Interest: name too short"});
292  return;
293  }
294 
295  SignatureInfo si;
296  try {
298  }
299  catch (const tlv::Error& e) {
300  state->fail({ValidationError::Code::INVALID_KEY_LOCATOR,
301  "Invalid signed Interest: " + std::string(e.what())});
302  return;
303  }
304 
306  }
307 
308  if (rule->check(tlv::Interest, sigType, interest.getName(), klName, state)) {
309  return continueValidation(make_shared<CertificateRequest>(klName), state);
310  }
311  // rule->check calls state->fail(...) if the check fails
312  return;
313  }
314  }
315 
316  return state->fail({ValidationError::POLICY_ERROR,
317  "No rule matched for interest `" + interest.getName().toUri() + "`"});
318 }
319 
320 } // namespace validator_config
321 } // inline namespace v2
322 } // namespace security
323 } // namespace ndn
boost::chrono::seconds seconds
Definition: time.hpp:47
boost::chrono::minutes minutes
Definition: time.hpp:46
Sign Interest using Packet Specification v0.3 semantics.
Copyright (c) 2011-2015 Regents of the University of California.
Represents a SignatureInfo or InterestSignatureInfo TLV element.
std::string to_string(const T &val)
Definition: backports.hpp:86
static unique_ptr< Rule > create(const ConfigSection &configSection, const std::string &configFilename)
create a rule from configuration section
Definition: rule.cpp:107
int32_t getSignatureType() const noexcept
Get SignatureType.
const size_t MIN_SIZE
minimal number of components for Signed Interest
Represents an Interest packet.
Definition: interest.hpp:48
optional< SignatureInfo > getSignatureInfo() const
Get the InterestSignatureInfo.
Definition: interest.cpp:544
#define NDN_THROW(e)
Definition: exception.hpp:61
provides a tag type for simple types
Definition: tag.hpp:58
const Component & at(ssize_t i) const
Returns an immutable reference to the component at the specified index, with bounds checking...
Definition: name.cpp:171
int32_t getSignatureType() const noexcept
Get SignatureType.
Definition: data.hpp:306
std::function< void(const shared_ptr< CertificateRequest > &certRequest, const shared_ptr< ValidationState > &state)> ValidationContinuation
static Name getKeyLocatorName(const SignatureInfo &si, ValidationState &state)
const Name & getName() const noexcept
Get name.
Definition: data.hpp:127
Represents an absolute name.
Definition: name.hpp:41
SignatureTypeValue
SignatureType values.
Definition: tlv.hpp:132
size_t size() const
Returns the number of components.
Definition: name.hpp:151
boost::property_tree::ptree ConfigSection
Definition: common.hpp:36
bool hasInnerPolicy() const
Check if inner policy is set.
void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation) override
Check data against the policy.
Block blockFromValue() const
Return a new Block constructed from the TLV-VALUE of this Block.
Definition: block.cpp:312
boost::chrono::hours hours
Definition: time.hpp:45
const ssize_t POS_SIG_INFO
const Name & getName() const noexcept
Definition: interest.hpp:172
void toUri(std::ostream &os, name::UriFormat format=name::UriFormat::DEFAULT) const
Write URI representation of the name to the output stream.
Definition: name.cpp:349
Represents a Data packet.
Definition: data.hpp:37
void load(const std::string &filename)
Load policy from file filename.
void wireDecode(const Block &wire, Type type=Type::Data)
Decode from wire format.
boost::chrono::nanoseconds nanoseconds
Definition: time.hpp:50
represents an error in TLV encoding or decoding
Definition: tlv.hpp:52