blob: ba87a2311f3000226688cac630c89fcabda62e26 [file] [log] [blame]
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -08001/* -*- 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
22#include "validation-policy-config.hpp"
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
31namespace ndn {
32namespace security {
33namespace v2 {
34namespace validator_config {
35
36ValidationPolicyConfig::ValidationPolicyConfig()
37 : m_shouldBypass(false)
38 , m_isConfigured(false)
39{
40}
41
42void
43ValidationPolicyConfig::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
56void
57ValidationPolicyConfig::load(const std::string& input, const std::string& filename)
58{
59 std::istringstream inputStream(input);
60 load(inputStream, filename);
61}
62
63void
64ValidationPolicyConfig::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
81void
82ValidationPolicyConfig::load(const ConfigSection& configSection,
83 const std::string& filename)
84{
85 if (m_isConfigured) {
86 BOOST_THROW_EXCEPTION(std::logic_error("ValidationPolicyConfig can be configured only once"));
87 }
88 m_isConfigured = true;
89
90 BOOST_ASSERT(!filename.empty());
91
92 if (configSection.begin() == configSection.end()) {
93 std::string msg = "Error processing configuration file";
94 msg += ": ";
95 msg += filename;
96 msg += " no data";
97 BOOST_THROW_EXCEPTION(Error(msg));
98 }
99
100 for (const auto& subSection : configSection) {
101 const std::string& sectionName = subSection.first;
102 const ConfigSection& section = subSection.second;
103
104 if (boost::iequals(sectionName, "rule")) {
105 auto rule = Rule::create(section, filename);
106 if (rule->getPktType() == tlv::Data) {
107 m_dataRules.push_back(std::move(rule));
108 }
109 else if (rule->getPktType() == tlv::Interest) {
110 m_interestRules.push_back(std::move(rule));
111 }
112 }
113 else if (boost::iequals(sectionName, "trust-anchor")) {
114 processConfigTrustAnchor(section, filename);
115 }
116 else {
117 std::string msg = "Error processing configuration file";
118 msg += " ";
119 msg += filename;
120 msg += " unrecognized section: " + sectionName;
121 BOOST_THROW_EXCEPTION(Error(msg));
122 }
123 }
124}
125
126void
127ValidationPolicyConfig::processConfigTrustAnchor(const ConfigSection& configSection, const std::string& filename)
128{
129 using namespace boost::filesystem;
130
131 ConfigSection::const_iterator propertyIt = configSection.begin();
132
133 // Get trust-anchor.type
134 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type")) {
135 BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.type>"));
136 }
137
138 std::string type = propertyIt->second.data();
139 propertyIt++;
140
141 if (boost::iequals(type, "file")) {
142 // Get trust-anchor.file
143 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name")) {
144 BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.file-name>"));
145 }
146
147 std::string file = propertyIt->second.data();
148 propertyIt++;
149
150 time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
151 if (propertyIt != configSection.end()) {
152 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
153 }
154
155 m_validator->loadAnchor(filename, absolute(file, path(filename).parent_path()).string(),
156 refresh, false);
157 return;
158 }
159 else if (boost::iequals(type, "base64")) {
160 // Get trust-anchor.base64-string
161 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
162 BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.base64-string>"));
163
164 std::stringstream ss(propertyIt->second.data());
165 propertyIt++;
166
167 // Check other stuff
168 if (propertyIt != configSection.end())
169 BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
170
171 auto idCert = io::load<Certificate>(ss);
172 if (idCert != nullptr) {
173 m_validator->loadAnchor("", std::move(*idCert));
174 }
175 else {
176 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
177 }
178
179 return;
180 }
181 else if (boost::iequals(type, "dir")) {
182 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
183 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
184
185 std::string dirString(propertyIt->second.data());
186 propertyIt++;
187
188 time::nanoseconds refresh = getRefreshPeriod(propertyIt, configSection.end());
189 if (propertyIt != configSection.end()) {
190 BOOST_THROW_EXCEPTION(Error("Expecting the end of trust-anchor"));
191 }
192
193 path dirPath = absolute(dirString, path(filename).parent_path());
194 m_validator->loadAnchor(dirString, dirPath.string(), refresh, true);
195 return;
196 }
197 else if (boost::iequals(type, "any")) {
198 m_shouldBypass = true;
199 }
200 else {
201 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
202 }
203}
204
205time::nanoseconds
206ValidationPolicyConfig::getRefreshPeriod(ConfigSection::const_iterator& it,
207 const ConfigSection::const_iterator& end)
208{
209 time::nanoseconds refresh = time::nanoseconds::max();
210 if (it == end) {
211 return refresh;
212 }
213
214 if (!boost::iequals(it->first, "refresh")) {
215 BOOST_THROW_EXCEPTION(Error("Expecting <trust-anchor.refresh>"));
216 }
217
218 std::string inputString = it->second.data();
219 ++it;
220
221 char unit = inputString[inputString.size() - 1];
222 std::string refreshString = inputString.substr(0, inputString.size() - 1);
223
224 uint32_t refreshPeriod = 0;
225
226 try {
227 refreshPeriod = boost::lexical_cast<uint32_t>(refreshString);
228 }
229 catch (const boost::bad_lexical_cast&) {
230 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
231 }
232
233 if (refreshPeriod == 0) {
234 return getDefaultRefreshPeriod();
235 }
236
237 switch (unit) {
238 case 'h':
239 return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod));
240 case 'm':
241 return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod));
242 case 's':
243 return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod));
244 default:
245 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
246 }
247}
248
249time::nanoseconds
250ValidationPolicyConfig::getDefaultRefreshPeriod()
251{
252 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
253}
254
255void
256ValidationPolicyConfig::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
257 const ValidationContinuation& continueValidation)
258{
259 BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
260
261 if (m_shouldBypass) {
262 return continueValidation(nullptr, state);
263 }
264
265 Name klName = getKeyLocatorName(data, *state);
266 if (!state->getOutcome()) { // already failed
267 return;
268 }
269
270 for (const auto& rule : m_dataRules) {
271 if (rule->match(tlv::Data, data.getName())) {
272 if (rule->check(tlv::Data, data.getName(), klName, state)) {
273 return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
274 }
275 // rule->check calls state->fail(...) if the check fails
276 return;
277 }
278 }
279
280 return state->fail({ValidationError::POLICY_ERROR, "No rule matched for data `" + data.getName().toUri() + "`"});
281}
282
283void
284ValidationPolicyConfig::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
285 const ValidationContinuation& continueValidation)
286{
287 BOOST_ASSERT_MSG(!hasInnerPolicy(), "ValidationPolicyConfig must be a terminal inner policy");
288
289 if (m_shouldBypass) {
290 return continueValidation(nullptr, state);
291 }
292
293 Name klName = getKeyLocatorName(interest, *state);
294 if (!state->getOutcome()) { // already failed
295 return;
296 }
297
298 for (const auto& rule : m_interestRules) {
299 if (rule->match(tlv::Interest, interest.getName())) {
300 if (rule->check(tlv::Interest, interest.getName(), klName, state)) {
301 return continueValidation(make_shared<CertificateRequest>(Interest(klName)), state);
302 }
303 // rule->check calls state->fail(...) if the check fails
304 return;
305 }
306 }
307
308 return state->fail({ValidationError::POLICY_ERROR, "No rule matched for interest `" + interest.getName().toUri() + "`"});
309}
310
311} // namespace validator_config
312} // namespace v2
313} // namespace security
314} // namespace ndn