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