blob: a0a35d8d251303c3622e671d239621886c2ed419 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Yingdi Yu48e8c0c2014-03-19 12:01:55 -07002/**
Alexander Afanasyevc169a812014-05-20 20:37:29 -04003 * Copyright (c) 2013-2014 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -070020 *
21 * @author Yingdi Yu <http://irl.cs.ucla.edu/~yingdi/>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070022 */
23
24#include "validator-config.hpp"
25#include "certificate-cache-ttl.hpp"
26#include "../util/io.hpp"
27
28#include <boost/filesystem.hpp>
29#include <boost/property_tree/info_parser.hpp>
30#include <boost/algorithm/string.hpp>
31
32namespace ndn {
33
34const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE;
35
Yingdi Yu96e64062014-04-15 19:57:33 -070036ValidatorConfig::ValidatorConfig(Face& face,
37 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070038 const int stepLimit)
39 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070040 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070041 , m_stepLimit(stepLimit)
42 , m_certificateCache(certificateCache)
43{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070044 if (!static_cast<bool>(m_certificateCache))
Alexander Afanasyevb67090a2014-04-29 22:31:01 -070045 m_certificateCache = make_shared<CertificateCacheTtl>(ref(m_face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070046}
47
48void
49ValidatorConfig::load(const std::string& filename)
50{
51 std::ifstream inputFile;
52 inputFile.open(filename.c_str());
53 if (!inputFile.good() || !inputFile.is_open())
54 {
55 std::string msg = "Failed to read configuration file: ";
56 msg += filename;
57 throw security::conf::Error(msg);
58 }
59 load(inputFile, filename);
60 inputFile.close();
61}
62
63void
64ValidatorConfig::load(const std::string& input, const std::string& filename)
65{
66 std::istringstream inputStream(input);
67 load(inputStream, filename);
68}
69
70
71void
72ValidatorConfig::load(std::istream& input, const std::string& filename)
73{
74 security::conf::ConfigSection tree;
75 try
76 {
77 boost::property_tree::read_info(input, tree);
78 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -070079 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070080 {
81 std::stringstream msg;
82 msg << "Failed to parse configuration file";
83 msg << " " << filename;
84 msg << " " << error.message() << " line " << error.line();
85 throw security::conf::Error(msg.str());
86 }
87
Yingdi Yudfa9d732014-04-09 09:53:01 -070088 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070089}
90
91void
Yingdi Yudfa9d732014-04-09 09:53:01 -070092ValidatorConfig::load(const security::conf::ConfigSection& configSection,
93 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070094{
95 BOOST_ASSERT(!filename.empty());
96
Yingdi Yu58f33712014-04-16 16:57:47 -070097 reset();
98
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070099 if (configSection.begin() == configSection.end())
100 {
101 std::string msg = "Error processing configuration file";
102 msg += ": ";
103 msg += filename;
104 msg += " no data";
105 throw security::conf::Error(msg);
106 }
107
108 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
109 i != configSection.end(); ++i)
110 {
111 const std::string& sectionName = i->first;
112 const security::conf::ConfigSection& section = i->second;
113
114 if (boost::iequals(sectionName, "rule"))
115 {
116 onConfigRule(section, filename);
117 }
118 else if (boost::iequals(sectionName, "trust-anchor"))
119 {
120 onConfigTrustAnchor(section, filename);
121 }
122 else
123 {
124 std::string msg = "Error processing configuration file";
125 msg += " ";
126 msg += filename;
127 msg += " unrecognized section: " + sectionName;
128 throw security::conf::Error(msg);
129 }
130 }
131}
132
133void
134ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
135 const std::string& filename)
136{
137 using namespace ndn::security::conf;
138
139 ConfigSection::const_iterator propertyIt = configSection.begin();
140
141 // Get rule.id
142 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
143 throw Error("Expect <rule.id>!");
144
145 std::string ruleId = propertyIt->second.data();
146 propertyIt++;
147
148 // Get rule.for
149 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
150 throw Error("Expect <rule.for> in rule: " + ruleId + "!");
151
152 std::string usage = propertyIt->second.data();
153 propertyIt++;
154
155 bool isForData;
156 if (boost::iequals(usage, "data"))
157 isForData = true;
158 else if (boost::iequals(usage, "interest"))
159 isForData = false;
160 else
161 throw Error("Unrecognized <rule.for>: " + usage
162 + " in rule: " + ruleId);
163
164 // Get rule.filter(s)
165 std::vector<shared_ptr<Filter> > filters;
166 for (; propertyIt != configSection.end(); propertyIt++)
167 {
168 if (!boost::iequals(propertyIt->first, "filter"))
169 {
170 if (boost::iequals(propertyIt->first, "checker"))
171 break;
172 throw Error("Expect <rule.filter> in rule: " + ruleId);
173 }
174
175 filters.push_back(FilterFactory::create(propertyIt->second));
176 continue;
177 }
178
179 // Get rule.checker(s)
180 std::vector<shared_ptr<Checker> > checkers;
181 for (; propertyIt != configSection.end(); propertyIt++)
182 {
183 if (!boost::iequals(propertyIt->first, "checker"))
184 throw Error("Expect <rule.checker> in rule: " + ruleId);
185
186 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
187 continue;
188 }
189
190 // Check other stuff
191 if (propertyIt != configSection.end())
192 throw Error("Expect the end of rule: " + ruleId);
193
194 if (checkers.size() == 0)
195 throw Error("No <rule.checker> is specified in rule: " + ruleId);
196
197 if (isForData)
198 {
199 shared_ptr<DataRule> rule(new DataRule(ruleId));
200 for (size_t i = 0; i < filters.size(); i++)
201 rule->addFilter(filters[i]);
202 for (size_t i = 0; i < checkers.size(); i++)
203 rule->addChecker(checkers[i]);
204
205 m_dataRules.push_back(rule);
206 }
207 else
208 {
209 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
210 for (size_t i = 0; i < filters.size(); i++)
211 rule->addFilter(filters[i]);
212 for (size_t i = 0; i < checkers.size(); i++)
213 rule->addChecker(checkers[i]);
214
215 m_interestRules.push_back(rule);
216 }
217}
218
219void
220ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
221 const std::string& filename)
222{
223 using namespace ndn::security::conf;
224 using namespace boost::filesystem;
225
226 ConfigSection::const_iterator propertyIt = configSection.begin();
227
228 // Get trust-anchor.type
229 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
230 throw Error("Expect <trust-anchor.type>!");
231
232 std::string type = propertyIt->second.data();
233 propertyIt++;
234
235 if (boost::iequals(type, "file"))
236 {
237 // Get trust-anchor.file
238 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
239 throw Error("Expect <trust-anchor.file-name>!");
240
241 std::string file = propertyIt->second.data();
242 propertyIt++;
243
244 // Check other stuff
245 if (propertyIt != configSection.end())
246 throw Error("Expect the end of trust-anchor!");
247
248 path certfilePath = absolute(file, path(filename).parent_path());
249 shared_ptr<IdentityCertificate> idCert =
250 io::load<IdentityCertificate>(certfilePath.string());
251
252 if (static_cast<bool>(idCert))
253 {
254 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700255 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700256 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
257 }
258 else
259 throw Error("Cannot read certificate from file: " +
260 certfilePath.native());
261
262 return;
263 }
264 else if (boost::iequals(type, "base64"))
265 {
266 // Get trust-anchor.base64-string
267 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
268 throw Error("Expect <trust-anchor.base64-string>!");
269
270 std::stringstream ss(propertyIt->second.data());
271 propertyIt++;
272
273 // Check other stuff
274 if (propertyIt != configSection.end())
275 throw Error("Expect the end of trust-anchor!");
276
277 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
278
279 if (static_cast<bool>(idCert))
280 {
281 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700282 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700283 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
284 }
285 else
286 throw Error("Cannot decode certificate from base64-string");
287
288 return;
289 }
Yingdi Yub4650652014-04-17 10:19:59 -0700290 else if (boost::iequals(type, "dir"))
291 {
292 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
293 throw Error("Expect <trust-anchor.dir>!");
294
295 std::string dirString(propertyIt->second.data());
296 propertyIt++;
297
298 if (propertyIt != configSection.end())
299 {
300 if (boost::iequals(propertyIt->first, "refresh"))
301 {
302 using namespace boost::filesystem;
303
304 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
305 propertyIt++;
306
307 if (propertyIt != configSection.end())
308 throw Error("Expect the end of trust-anchor!");
309
310 path dirPath = absolute(dirString, path(filename).parent_path());
311
312 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
313
314 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
315
316 return;
317 }
318 else
319 throw Error("Expect <trust-anchor.refresh>!");
320 }
321 else
322 {
323 using namespace boost::filesystem;
324
325 path dirPath = absolute(dirString, path(filename).parent_path());
326
327 directory_iterator end;
328
329 for (directory_iterator it(dirPath); it != end; it++)
330 {
331 shared_ptr<IdentityCertificate> idCert =
332 io::load<IdentityCertificate>(it->path().string());
333
334 if (static_cast<bool>(idCert))
335 m_staticContainer.add(idCert);
336 }
337
338 return;
339 }
340 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700341 else if (boost::iequals(type, "any"))
342 {
343 m_shouldValidate = false;
344 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700345 else
346 throw Error("Unsupported trust-anchor.type: " + type);
347}
348
Yingdi Yub4650652014-04-17 10:19:59 -0700349time::nanoseconds
350ValidatorConfig::getRefreshPeriod(std::string inputString)
351{
352 char unit = inputString[inputString.size() - 1];
353 std::string refreshString = inputString.substr(0, inputString.size() - 1);
354
355 uint32_t number;
356
357 try
358 {
359 number = boost::lexical_cast<uint32_t>(refreshString);
360 }
361 catch (boost::bad_lexical_cast&)
362 {
363 throw Error("Bad number: " + refreshString);
364 }
365
366 if (number == 0)
367 return getDefaultRefreshPeriod();
368
369 switch (unit)
370 {
371 case 'h':
372 return time::duration_cast<time::nanoseconds>(time::hours(number));
373 case 'm':
374 return time::duration_cast<time::nanoseconds>(time::minutes(number));
375 case 's':
376 return time::duration_cast<time::nanoseconds>(time::seconds(number));
377 default:
378 throw Error(std::string("Wrong time unit: ") + unit);
379 }
380}
381
382void
383ValidatorConfig::refreshAnchors()
384{
385 time::system_clock::TimePoint now = time::system_clock::now();
386
387 bool isRefreshed = false;
388
389 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
390 cIt != m_dynamicContainers.end(); cIt++)
391 {
392 if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now)
393 {
394 isRefreshed = true;
395 cIt->refresh();
396 cIt->setLastRefresh(now);
397 }
398 else
399 break;
400 }
401
402 if (isRefreshed)
403 {
404 m_anchors.clear();
405
406 for (CertificateList::const_iterator it = m_staticContainer.getAll().begin();
407 it != m_staticContainer.getAll().end(); it++)
408 {
409 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
410 }
411
412 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
413 cIt != m_dynamicContainers.end(); cIt++)
414 {
415 const CertificateList& certList = cIt->getAll();
416
417 for (CertificateList::const_iterator it = certList.begin();
418 it != certList.end(); it++)
419 {
420 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
421 }
422 }
423 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
424 }
425}
426
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700427void
428ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700429 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700430 const OnDataValidated& onValidated,
431 const OnDataValidationFailed& onValidationFailed,
432 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
433{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700434 if (!m_shouldValidate)
435 return onValidated(data.shared_from_this());
436
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700437 bool isMatched = false;
438 int8_t checkResult = -1;
439
440 for (DataRuleList::iterator it = m_dataRules.begin();
441 it != m_dataRules.end(); it++)
442 {
443 if ((*it)->match(data))
444 {
445 isMatched = true;
446 checkResult = (*it)->check(data, onValidated, onValidationFailed);
447 break;
448 }
449 }
450
451 if (!isMatched)
452 return onValidationFailed(data.shared_from_this(), "No rule matched!");
453
454 if (checkResult == 0)
455 {
456 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700457 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700458 onValidated, onValidationFailed, nextSteps);
459 }
460}
461
462void
463ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700464 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700465 const OnInterestValidated& onValidated,
466 const OnInterestValidationFailed& onValidationFailed,
467 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
468{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700469 if (!m_shouldValidate)
470 return onValidated(interest.shared_from_this());
471
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700472 bool isMatched = false;
473 int8_t checkResult = -1;
474
475 for (InterestRuleList::iterator it = m_interestRules.begin();
476 it != m_interestRules.end(); it++)
477 {
478 if ((*it)->match(interest))
479 {
480 isMatched = true;
481 checkResult = (*it)->check(interest, onValidated, onValidationFailed);
482 break;
483 }
484 }
485
486 if (!isMatched)
487 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
488
489 if (checkResult == 0)
490 {
491 const Name& interestName = interest.getName();
492 Name signedName = interestName.getPrefix(-2);
493 Signature signature(interestName[-2].blockFromValue(),
494 interestName[-1].blockFromValue());
495
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700496 checkSignature(interest, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700497 onValidated, onValidationFailed, nextSteps);
498 }
499}
500
Yingdi Yub4650652014-04-17 10:19:59 -0700501void
502ValidatorConfig::DynamicTrustAnchorContainer::refresh()
503{
504 using namespace boost::filesystem;
505
506 m_certificates.clear();
507
508 if (m_isDir)
509 {
510 directory_iterator end;
511
512 for (directory_iterator it(m_path); it != end; it++)
513 {
514 shared_ptr<IdentityCertificate> idCert =
515 io::load<IdentityCertificate>(it->path().string());
516
517 if (static_cast<bool>(idCert))
518 m_certificates.push_back(idCert);
519 }
520 }
521 else
522 {
523 shared_ptr<IdentityCertificate> idCert =
524 io::load<IdentityCertificate>(m_path.string());
525
526 if (static_cast<bool>(idCert))
527 m_certificates.push_back(idCert);
528 }
529}
530
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700531
532} // namespace ndn