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-2017 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 
23 #include "validator.hpp"
24 #include "../../util/io.hpp"
25 
26 #include <boost/algorithm/string.hpp>
27 #include <boost/filesystem.hpp>
28 #include <boost/lexical_cast.hpp>
29 #include <boost/property_tree/info_parser.hpp>
30 
31 namespace ndn {
32 namespace security {
33 namespace v2 {
34 namespace validator_config {
35 
37  : m_shouldBypass(false)
38  , m_isConfigured(false)
39 {
40 }
41 
42 void
43 ValidationPolicyConfig::load(const std::string& filename)
44 {
45  std::ifstream inputFile;
46  inputFile.open(filename.c_str());
47  if (!inputFile.good() || !inputFile.is_open()) {
48  std::string msg = "Failed to read configuration file: ";
49  msg += filename;
50  BOOST_THROW_EXCEPTION(Error(msg));
51  }
52  load(inputFile, filename);
53  inputFile.close();
54 }
55 
56 void
57 ValidationPolicyConfig::load(const std::string& input, const std::string& filename)
58 {
59  std::istringstream inputStream(input);
60  load(inputStream, filename);
61 }
62 
63 void
64 ValidationPolicyConfig::load(std::istream& input, const std::string& filename)
65 {
66  ConfigSection tree;
67  try {
68  boost::property_tree::read_info(input, tree);
69  }
70  catch (const boost::property_tree::info_parser_error& error) {
71  std::stringstream msg;
72  msg << "Failed to parse configuration file";
73  msg << " " << filename;
74  msg << " " << error.message() << " line " << error.line();
75  BOOST_THROW_EXCEPTION(Error(msg.str()));
76  }
77 
78  load(tree, filename);
79 }
80 
81 void
83  const std::string& filename)
84 {
85  if (m_isConfigured) {
86  m_shouldBypass = false;
87  m_dataRules.clear();
88  m_interestRules.clear();
89 
92  }
93  m_isConfigured = true;
94 
95  BOOST_ASSERT(!filename.empty());
96 
97  if (configSection.begin() == configSection.end()) {
98  std::string msg = "Error processing configuration file";
99  msg += ": ";
100  msg += filename;
101  msg += " no data";
102  BOOST_THROW_EXCEPTION(Error(msg));
103  }
104 
105  for (const auto& subSection : configSection) {
106  const std::string& sectionName = subSection.first;
107  const ConfigSection& section = subSection.second;
108 
109  if (boost::iequals(sectionName, "rule")) {
110  auto rule = Rule::create(section, filename);
111  if (rule->getPktType() == tlv::Data) {
112  m_dataRules.push_back(std::move(rule));
113  }
114  else if (rule->getPktType() == tlv::Interest) {
115  m_interestRules.push_back(std::move(rule));
116  }
117  }
118  else if (boost::iequals(sectionName, "trust-anchor")) {
119  processConfigTrustAnchor(section, filename);
120  }
121  else {
122  std::string msg = "Error processing configuration file";
123  msg += " ";
124  msg += filename;
125  msg += " unrecognized section: " + sectionName;
126  BOOST_THROW_EXCEPTION(Error(msg));
127  }
128  }
129 }
130 
131 void
132 ValidationPolicyConfig::processConfigTrustAnchor(const ConfigSection& configSection, const std::string& filename)
133 {
134  using namespace boost::filesystem;
135 
136  ConfigSection::const_iterator propertyIt = configSection.begin();
137 
138  // Get trust-anchor.type
139  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
140  BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.type>"));
141  }
142 
143  std::string type = propertyIt->second.data();
144  propertyIt++;
145 
146  if (boost::iequals(type, "file")) {
147  // Get trust-anchor.file
148  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name")) {
149  BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.file-name>"));
150  }
151 
152  std::string file = propertyIt->second.data();
153  propertyIt++;
154 
155  time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
156  if (propertyIt != configSection.end()) {
157  BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
158  }
159 
160  m_validator->loadAnchor(filename, absolute(file, path(filename).parent_path()).string(),
161  refresh, false);
162  return;
163  }
164  else if (boost::iequals(type, "base64")) {
165  // Get trust-anchor.base64-string
166  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
167  BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.base64-string>"));
168 
169  std::stringstream ss(propertyIt->second.data());
170  propertyIt++;
171 
172  // Check other stuff
173  if (propertyIt != configSection.end())
174  BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
175 
176  auto idCert = io::load<Certificate>(ss);
177  if (idCert != nullptr) {
178  m_validator->loadAnchor("", std::move(*idCert));
179  }
180  else {
181  BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
182  }
183 
184  return;
185  }
186  else if (boost::iequals(type, "dir")) {
187  if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
188  BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
189 
190  std::string dirString(propertyIt->second.data());
191  propertyIt++;
192 
193  time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
194  if (propertyIt != configSection.end()) {
195  BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
196  }
197 
198  path dirPath = absolute(dirString, path(filename).parent_path());
199  m_validator->loadAnchor(dirString, dirPath.string(), refresh, true);
200  return;
201  }
202  else if (boost::iequals(type, "any")) {
203  m_shouldBypass = true;
204  }
205  else {
206  BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
207  }
208 }
209 
210 time::nanoseconds
211 ValidationPolicyConfig::getRefreshPeriod(ConfigSection::const_iterator& it,
212  const ConfigSection::const_iterator& end)
213 {
214  time::nanoseconds refresh = time::nanoseconds::max();
215  if (it == end) {
216  return refresh;
217  }
218 
219  if (!boost::iequals(it->first, "refresh")) {
220  BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.refresh>"));
221  }
222 
223  std::string inputString = it->second.data();
224  ++it;
225 
226  char unit = inputString[inputString.size() - 1];
227  std::string refreshString = inputString.substr(0, inputString.size() - 1);
228 
229  uint32_t refreshPeriod = 0;
230 
231  try {
232  refreshPeriod = boost::lexical_cast<uint32_t>(refreshString);
233  }
234  catch (const boost::bad_lexical_cast&) {
235  BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
236  }
237 
238  if (refreshPeriod == 0) {
239  return getDefaultRefreshPeriod();
240  }
241 
242  switch (unit) {
243  case 'h':
244  return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod));
245  case 'm':
246  return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod));
247  case 's':
248  return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod));
249  default:
250  BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
251  }
252 }
253 
254 time::nanoseconds
255 ValidationPolicyConfig::getDefaultRefreshPeriod()
256 {
257  return time::duration_cast<time::nanoseconds>(time::seconds(3600));
258 }
259 
260 void
261 ValidationPolicyConfig::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
262  const ValidationContinuation& continueValidation)
263 {
264  BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
265 
266  if (m_shouldBypass) {
267  return continueValidation(nullptr, state);
268  }
269 
270  Name klName = getKeyLocatorName(data, *state);
271  if (!state->getOutcome()) { // already failed
272  return;
273  }
274 
275  for (const auto& rule : m_dataRules) {
276  if (rule->match(tlv::Data, data.getName())) {
277  if (rule->check(tlv::Data, data.getName(), klName, state)) {
278  return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
279  }
280  // rule->check calls state->fail(...) if the check fails
281  return;
282  }
283  }
284 
285  return state->fail({ValidationError::POLICY_ERROR, "No rule matched for data `" + data.getName().toUri() + "`"});
286 }
287 
288 void
289 ValidationPolicyConfig::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
290  const ValidationContinuation& continueValidation)
291 {
292  BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
293 
294  if (m_shouldBypass) {
295  return continueValidation(nullptr, state);
296  }
297 
298  Name klName = getKeyLocatorName(interest, *state);
299  if (!state->getOutcome()) { // already failed
300  return;
301  }
302 
303  for (const auto& rule : m_interestRules) {
304  if (rule->match(tlv::Interest, interest.getName())) {
305  if (rule->check(tlv::Interest, interest.getName(), klName, state)) {
306  return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
307  }
308  // rule->check calls state->fail(...) if the check fails
309  return;
310  }
311  }
312 
313  return state->fail({ValidationError::POLICY_ERROR, "No rule matched for interest `" + interest.getName().toUri() + "`"});
314 }
315 
316 } // namespace validator_config
317 } // namespace v2
318 } // namespace security
319 } // namespace ndn
const Name & getName() const
Definition: interest.hpp:139
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:66
static unique_ptr< Rule > create(const ConfigSection &configSection, const std::string &configFilename)
create a rule from configuration section
Definition: rule.cpp:98
bool hasInnerPolicy() const
Check if inner policy is set.
represents an Interest packet
Definition: interest.hpp:42
std::string toUri() const
Get URI representation of the name.
Definition: name.cpp:122
std::function< void(const shared_ptr< CertificateRequest > &certRequest, const shared_ptr< ValidationState > &state)> ValidationContinuation
static Name getKeyLocatorName(const SignatureInfo &si, ValidationState &state)
void loadAnchor(const std::string &groupId, Certificate &&cert)
load static trust anchor.
Definition: validator.cpp:185
Represents an absolute name.
Definition: name.hpp:42
void resetVerifiedCertificates()
Remove any cached verified certificates.
Definition: validator.cpp:210
const Name & getName() const
Get name.
Definition: data.hpp:121
void resetAnchors()
remove any previously loaded static or dynamic trust anchor
Definition: validator.cpp:198
void checkPolicy(const Data &data, const shared_ptr< ValidationState > &state, const ValidationContinuation &continueValidation) override
Check data against the policy.
Represents a Data packet.
Definition: data.hpp:35