ndn-cxx 0.9.0-42-g8cff8f06
Loading...
Searching...
No Matches
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-2026 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
35void
36ValidationPolicyConfig::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
45void
46ValidationPolicyConfig::load(const std::string& input, const std::string& filename)
47{
48 std::istringstream inputStream(input);
49 load(inputStream, filename);
50}
51
52void
53ValidationPolicyConfig::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
66void
67ValidationPolicyConfig::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
106void
107ValidationPolicyConfig::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 try {
148 m_validator->loadAnchor("", io::loadTlv<Certificate>(ss, io::BASE64));
149 }
150 catch (const io::Error& e) {
151 NDN_THROW_NESTED(Error("Cannot load certificate from base64-string: "s + e.what()));
152 }
153 }
154 else if (boost::iequals(type, "dir")) {
155 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
156 NDN_THROW(Error("Expecting <trust-anchor.dir>"));
157
158 std::string dirString(propertyIt->second.data());
159 propertyIt++;
160
161 time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
162 if (propertyIt != configSection.end())
163 NDN_THROW(Error("Expecting end of <trust-anchor>"));
164
165 m_validator->loadAnchor(dirString, baseDir / dirString, refresh, true);
166 }
167 else if (boost::iequals(type, "any")) {
168 m_shouldBypass = true;
169 }
170 else {
171 NDN_THROW(Error("Unrecognized <trust-anchor.type>: " + type));
172 }
173}
174
176ValidationPolicyConfig::getRefreshPeriod(ConfigSection::const_iterator& it,
177 const ConfigSection::const_iterator& end)
178{
179 auto refresh = time::nanoseconds::max();
180 if (it == end) {
181 return refresh;
182 }
183
184 if (!boost::iequals(it->first, "refresh")) {
185 NDN_THROW(Error("Expecting <trust-anchor.refresh>"));
186 }
187
188 std::string inputString = it->second.data();
189 ++it;
190 char unit = inputString[inputString.size() - 1];
191 std::string refreshString = inputString.substr(0, inputString.size() - 1);
192
193 int32_t refreshPeriod = -1;
194 try {
195 refreshPeriod = boost::lexical_cast<int32_t>(refreshString);
196 }
197 catch (const boost::bad_lexical_cast&) {
198 // pass
199 }
200 if (refreshPeriod < 0) {
201 NDN_THROW(Error("Bad refresh value: " + refreshString));
202 }
203
204 if (refreshPeriod == 0) {
205 return getDefaultRefreshPeriod();
206 }
207
208 switch (unit) {
209 case 'h':
210 return time::hours(refreshPeriod);
211 case 'm':
212 return time::minutes(refreshPeriod);
213 case 's':
214 return time::seconds(refreshPeriod);
215 default:
216 NDN_THROW(Error("Bad refresh time unit: "s + unit));
217 }
218}
219
221ValidationPolicyConfig::getDefaultRefreshPeriod()
222{
223 return 1_h;
224}
225
226void
227ValidationPolicyConfig::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
228 const ValidationContinuation& continueValidation)
229{
230 BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
231
232 if (m_shouldBypass) {
233 return continueValidation(nullptr, state);
234 }
235
236 Name klName = getKeyLocatorName(data.getSignatureInfo(), *state);
237 if (!state->getOutcome()) { // already failed
238 return;
239 }
240
241 auto sigType = tlv::SignatureTypeValue(data.getSignatureType());
242
243 for (const auto& rule : m_dataRules) {
244 if (rule->match(tlv::Data, data.getName(), state)) {
245 if (rule->check(tlv::Data, sigType, data.getName(), klName, state)) {
246 return continueValidation(make_shared<CertificateRequest>(klName), state);
247 }
248 // rule->check calls state->fail(...) if the check fails
249 return;
250 }
251 }
252
253 return state->fail({ValidationError::POLICY_ERROR,
254 "No rule matched for data `" + data.getName().toUri() + "`"});
255}
256
257void
258ValidationPolicyConfig::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
259 const ValidationContinuation& continueValidation)
260{
261 BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
262
263 if (m_shouldBypass) {
264 return continueValidation(nullptr, state);
265 }
266
267 auto sigInfo = getSignatureInfo(interest, *state);
268 if (!state->getOutcome()) { // already failed
269 return;
270 }
271
272 Name klName = getKeyLocatorName(sigInfo, *state);
273 if (!state->getOutcome()) { // already failed
274 return;
275 }
276
277 auto sigType = tlv::SignatureTypeValue(sigInfo.getSignatureType());
278
279 for (const auto& rule : m_interestRules) {
280 if (rule->match(tlv::Interest, interest.getName(), state)) {
281 if (rule->check(tlv::Interest, sigType, interest.getName(), klName, state)) {
282 return continueValidation(make_shared<CertificateRequest>(klName), state);
283 }
284 // rule->check calls state->fail(...) if the check fails
285 return;
286 }
287 }
288
289 return state->fail({ValidationError::POLICY_ERROR,
290 "No rule matched for interest `" + interest.getName().toUri() + "`"});
291}
292
293} // 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 Name & getName() const noexcept
Get the Data name.
Definition data.hpp:137
const SignatureInfo & getSignatureInfo() const noexcept
Get the SignatureInfo element.
Definition data.hpp:243
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.
void resetVerifiedCertificates()
Remove any cached verified certificates.
void resetAnchors()
Remove any previously loaded static or dynamic trust anchor.
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_NESTED(e)
Definition exception.hpp:65
#define NDN_THROW(e)
Definition exception.hpp:56
@ BASE64
Base64 encoding.
Definition io.hpp:53
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