blob: 80d2fb1bb656456d9d39458f1727bdfc545a6d8c [file] [log] [blame]
Yingdi Yu48e8c0c2014-03-19 12:01:55 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
2/**
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07003 * Copyright (c) 2013-2014, Regents of the University of California.
4 * All rights reserved.
5 *
6 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
7 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
8 *
9 * This file licensed under New BSD License. See COPYING for detailed information about
10 * ndn-cxx library copyright, permissions, and redistribution restrictions.
11 *
12 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070013 */
14
15#include "validator-config.hpp"
16#include "certificate-cache-ttl.hpp"
17#include "../util/io.hpp"
18
19#include <boost/filesystem.hpp>
20#include <boost/property_tree/info_parser.hpp>
21#include <boost/algorithm/string.hpp>
22
23namespace ndn {
24
25const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE;
26
Yingdi Yu96e64062014-04-15 19:57:33 -070027ValidatorConfig::ValidatorConfig(Face& face,
28 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070029 const int stepLimit)
30 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070031 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070032 , m_stepLimit(stepLimit)
33 , m_certificateCache(certificateCache)
34{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070035 if (!static_cast<bool>(m_certificateCache))
Yingdi Yu58f33712014-04-16 16:57:47 -070036 m_certificateCache = make_shared<CertificateCacheTtl>(boost::ref(m_face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070037}
38
39void
40ValidatorConfig::load(const std::string& filename)
41{
42 std::ifstream inputFile;
43 inputFile.open(filename.c_str());
44 if (!inputFile.good() || !inputFile.is_open())
45 {
46 std::string msg = "Failed to read configuration file: ";
47 msg += filename;
48 throw security::conf::Error(msg);
49 }
50 load(inputFile, filename);
51 inputFile.close();
52}
53
54void
55ValidatorConfig::load(const std::string& input, const std::string& filename)
56{
57 std::istringstream inputStream(input);
58 load(inputStream, filename);
59}
60
61
62void
63ValidatorConfig::load(std::istream& input, const std::string& filename)
64{
65 security::conf::ConfigSection tree;
66 try
67 {
68 boost::property_tree::read_info(input, tree);
69 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -070070 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070071 {
72 std::stringstream msg;
73 msg << "Failed to parse configuration file";
74 msg << " " << filename;
75 msg << " " << error.message() << " line " << error.line();
76 throw security::conf::Error(msg.str());
77 }
78
Yingdi Yudfa9d732014-04-09 09:53:01 -070079 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070080}
81
82void
Yingdi Yudfa9d732014-04-09 09:53:01 -070083ValidatorConfig::load(const security::conf::ConfigSection& configSection,
84 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070085{
86 BOOST_ASSERT(!filename.empty());
87
Yingdi Yu58f33712014-04-16 16:57:47 -070088 reset();
89
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070090 if (configSection.begin() == configSection.end())
91 {
92 std::string msg = "Error processing configuration file";
93 msg += ": ";
94 msg += filename;
95 msg += " no data";
96 throw security::conf::Error(msg);
97 }
98
99 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
100 i != configSection.end(); ++i)
101 {
102 const std::string& sectionName = i->first;
103 const security::conf::ConfigSection& section = i->second;
104
105 if (boost::iequals(sectionName, "rule"))
106 {
107 onConfigRule(section, filename);
108 }
109 else if (boost::iequals(sectionName, "trust-anchor"))
110 {
111 onConfigTrustAnchor(section, filename);
112 }
113 else
114 {
115 std::string msg = "Error processing configuration file";
116 msg += " ";
117 msg += filename;
118 msg += " unrecognized section: " + sectionName;
119 throw security::conf::Error(msg);
120 }
121 }
122}
123
124void
125ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
126 const std::string& filename)
127{
128 using namespace ndn::security::conf;
129
130 ConfigSection::const_iterator propertyIt = configSection.begin();
131
132 // Get rule.id
133 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
134 throw Error("Expect <rule.id>!");
135
136 std::string ruleId = propertyIt->second.data();
137 propertyIt++;
138
139 // Get rule.for
140 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
141 throw Error("Expect <rule.for> in rule: " + ruleId + "!");
142
143 std::string usage = propertyIt->second.data();
144 propertyIt++;
145
146 bool isForData;
147 if (boost::iequals(usage, "data"))
148 isForData = true;
149 else if (boost::iequals(usage, "interest"))
150 isForData = false;
151 else
152 throw Error("Unrecognized <rule.for>: " + usage
153 + " in rule: " + ruleId);
154
155 // Get rule.filter(s)
156 std::vector<shared_ptr<Filter> > filters;
157 for (; propertyIt != configSection.end(); propertyIt++)
158 {
159 if (!boost::iequals(propertyIt->first, "filter"))
160 {
161 if (boost::iequals(propertyIt->first, "checker"))
162 break;
163 throw Error("Expect <rule.filter> in rule: " + ruleId);
164 }
165
166 filters.push_back(FilterFactory::create(propertyIt->second));
167 continue;
168 }
169
170 // Get rule.checker(s)
171 std::vector<shared_ptr<Checker> > checkers;
172 for (; propertyIt != configSection.end(); propertyIt++)
173 {
174 if (!boost::iequals(propertyIt->first, "checker"))
175 throw Error("Expect <rule.checker> in rule: " + ruleId);
176
177 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
178 continue;
179 }
180
181 // Check other stuff
182 if (propertyIt != configSection.end())
183 throw Error("Expect the end of rule: " + ruleId);
184
185 if (checkers.size() == 0)
186 throw Error("No <rule.checker> is specified in rule: " + ruleId);
187
188 if (isForData)
189 {
190 shared_ptr<DataRule> rule(new DataRule(ruleId));
191 for (size_t i = 0; i < filters.size(); i++)
192 rule->addFilter(filters[i]);
193 for (size_t i = 0; i < checkers.size(); i++)
194 rule->addChecker(checkers[i]);
195
196 m_dataRules.push_back(rule);
197 }
198 else
199 {
200 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
201 for (size_t i = 0; i < filters.size(); i++)
202 rule->addFilter(filters[i]);
203 for (size_t i = 0; i < checkers.size(); i++)
204 rule->addChecker(checkers[i]);
205
206 m_interestRules.push_back(rule);
207 }
208}
209
210void
211ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
212 const std::string& filename)
213{
214 using namespace ndn::security::conf;
215 using namespace boost::filesystem;
216
217 ConfigSection::const_iterator propertyIt = configSection.begin();
218
219 // Get trust-anchor.type
220 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
221 throw Error("Expect <trust-anchor.type>!");
222
223 std::string type = propertyIt->second.data();
224 propertyIt++;
225
226 if (boost::iequals(type, "file"))
227 {
228 // Get trust-anchor.file
229 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
230 throw Error("Expect <trust-anchor.file-name>!");
231
232 std::string file = propertyIt->second.data();
233 propertyIt++;
234
235 // Check other stuff
236 if (propertyIt != configSection.end())
237 throw Error("Expect the end of trust-anchor!");
238
239 path certfilePath = absolute(file, path(filename).parent_path());
240 shared_ptr<IdentityCertificate> idCert =
241 io::load<IdentityCertificate>(certfilePath.string());
242
243 if (static_cast<bool>(idCert))
244 {
245 BOOST_ASSERT(idCert->getName().size() >= 1);
246 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
247 }
248 else
249 throw Error("Cannot read certificate from file: " +
250 certfilePath.native());
251
252 return;
253 }
254 else if (boost::iequals(type, "base64"))
255 {
256 // Get trust-anchor.base64-string
257 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
258 throw Error("Expect <trust-anchor.base64-string>!");
259
260 std::stringstream ss(propertyIt->second.data());
261 propertyIt++;
262
263 // Check other stuff
264 if (propertyIt != configSection.end())
265 throw Error("Expect the end of trust-anchor!");
266
267 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
268
269 if (static_cast<bool>(idCert))
270 {
271 BOOST_ASSERT(idCert->getName().size() >= 1);
272 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
273 }
274 else
275 throw Error("Cannot decode certificate from base64-string");
276
277 return;
278 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700279 else if (boost::iequals(type, "any"))
280 {
281 m_shouldValidate = false;
282 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700283 else
284 throw Error("Unsupported trust-anchor.type: " + type);
285}
286
287void
288ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700289 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700290 const OnDataValidated& onValidated,
291 const OnDataValidationFailed& onValidationFailed,
292 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
293{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700294 if (!m_shouldValidate)
295 return onValidated(data.shared_from_this());
296
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700297 if (m_stepLimit == nSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700298 return onValidationFailed(data.shared_from_this(),
299 "Maximum steps of validation reached");
300
301 bool isMatched = false;
302 int8_t checkResult = -1;
303
304 for (DataRuleList::iterator it = m_dataRules.begin();
305 it != m_dataRules.end(); it++)
306 {
307 if ((*it)->match(data))
308 {
309 isMatched = true;
310 checkResult = (*it)->check(data, onValidated, onValidationFailed);
311 break;
312 }
313 }
314
315 if (!isMatched)
316 return onValidationFailed(data.shared_from_this(), "No rule matched!");
317
318 if (checkResult == 0)
319 {
320 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700321 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700322 onValidated, onValidationFailed, nextSteps);
323 }
324}
325
326void
327ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700328 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700329 const OnInterestValidated& onValidated,
330 const OnInterestValidationFailed& onValidationFailed,
331 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
332{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700333 if (!m_shouldValidate)
334 return onValidated(interest.shared_from_this());
335
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700336 if (m_stepLimit == nSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700337 return onValidationFailed(interest.shared_from_this(),
338 "Maximum steps of validation reached");
339
340 bool isMatched = false;
341 int8_t checkResult = -1;
342
343 for (InterestRuleList::iterator it = m_interestRules.begin();
344 it != m_interestRules.end(); it++)
345 {
346 if ((*it)->match(interest))
347 {
348 isMatched = true;
349 checkResult = (*it)->check(interest, onValidated, onValidationFailed);
350 break;
351 }
352 }
353
354 if (!isMatched)
355 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
356
357 if (checkResult == 0)
358 {
359 const Name& interestName = interest.getName();
360 Name signedName = interestName.getPrefix(-2);
361 Signature signature(interestName[-2].blockFromValue(),
362 interestName[-1].blockFromValue());
363
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700364 checkSignature(interest, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700365 onValidated, onValidationFailed, nextSteps);
366 }
367}
368
369
370} // namespace ndn