blob: ba37a5a25f424a29e95c84be060fe504376dab9d [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/**
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -07003 * Copyright (c) 2013-2016 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/>
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -070022 * @author Zhiyi Zhang <zhangzhiyi1919@gmail.com>
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070023 */
24
25#include "validator-config.hpp"
26#include "certificate-cache-ttl.hpp"
27#include "../util/io.hpp"
28
29#include <boost/filesystem.hpp>
30#include <boost/property_tree/info_parser.hpp>
31#include <boost/algorithm/string.hpp>
32
33namespace ndn {
34
35const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE;
Yingdi Yu0f5fb692014-06-10 12:07:28 -070036const time::milliseconds ValidatorConfig::DEFAULT_GRACE_INTERVAL(3000);
37const time::system_clock::Duration ValidatorConfig::DEFAULT_KEY_TIMESTAMP_TTL = time::hours(1);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070038
Yingdi Yu4e9b0692014-11-04 16:13:56 -080039ValidatorConfig::ValidatorConfig(Face* face,
40 const shared_ptr<CertificateCache>& certificateCache,
41 const time::milliseconds& graceInterval,
42 const size_t stepLimit,
43 const size_t maxTrackedKeys,
44 const time::system_clock::Duration& keyTimestampTtl)
45 : Validator(face)
46 , m_shouldValidate(true)
47 , m_stepLimit(stepLimit)
48 , m_certificateCache(certificateCache)
49 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
50 DEFAULT_GRACE_INTERVAL : graceInterval)
51 , m_maxTrackedKeys(maxTrackedKeys)
52 , m_keyTimestampTtl(keyTimestampTtl)
53{
54 if (!static_cast<bool>(m_certificateCache) && face != nullptr)
55 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->getIoService()));
56}
57
Yingdi Yu96e64062014-04-15 19:57:33 -070058ValidatorConfig::ValidatorConfig(Face& face,
59 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu0f5fb692014-06-10 12:07:28 -070060 const time::milliseconds& graceInterval,
61 const size_t stepLimit,
62 const size_t maxTrackedKeys,
63 const time::system_clock::Duration& keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070064 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070065 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070066 , m_stepLimit(stepLimit)
67 , m_certificateCache(certificateCache)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070068 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
69 DEFAULT_GRACE_INTERVAL : graceInterval)
70 , m_maxTrackedKeys(maxTrackedKeys)
71 , m_keyTimestampTtl(keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070072{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070073 if (!static_cast<bool>(m_certificateCache))
Yingdi Yu4e9b0692014-11-04 16:13:56 -080074 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070075}
76
77void
78ValidatorConfig::load(const std::string& filename)
79{
80 std::ifstream inputFile;
81 inputFile.open(filename.c_str());
82 if (!inputFile.good() || !inputFile.is_open())
83 {
84 std::string msg = "Failed to read configuration file: ";
85 msg += filename;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070086 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070087 }
88 load(inputFile, filename);
89 inputFile.close();
90}
91
92void
93ValidatorConfig::load(const std::string& input, const std::string& filename)
94{
95 std::istringstream inputStream(input);
96 load(inputStream, filename);
97}
98
99
100void
101ValidatorConfig::load(std::istream& input, const std::string& filename)
102{
103 security::conf::ConfigSection tree;
104 try
105 {
106 boost::property_tree::read_info(input, tree);
107 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700108 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700109 {
110 std::stringstream msg;
111 msg << "Failed to parse configuration file";
112 msg << " " << filename;
113 msg << " " << error.message() << " line " << error.line();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700114 BOOST_THROW_EXCEPTION(security::conf::Error(msg.str()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700115 }
116
Yingdi Yudfa9d732014-04-09 09:53:01 -0700117 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700118}
119
120void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700121ValidatorConfig::load(const security::conf::ConfigSection& configSection,
122 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700123{
124 BOOST_ASSERT(!filename.empty());
125
Yingdi Yu58f33712014-04-16 16:57:47 -0700126 reset();
127
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700128 if (configSection.begin() == configSection.end())
129 {
130 std::string msg = "Error processing configuration file";
131 msg += ": ";
132 msg += filename;
133 msg += " no data";
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700134 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700135 }
136
137 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
138 i != configSection.end(); ++i)
139 {
140 const std::string& sectionName = i->first;
141 const security::conf::ConfigSection& section = i->second;
142
143 if (boost::iequals(sectionName, "rule"))
144 {
145 onConfigRule(section, filename);
146 }
147 else if (boost::iequals(sectionName, "trust-anchor"))
148 {
149 onConfigTrustAnchor(section, filename);
150 }
151 else
152 {
153 std::string msg = "Error processing configuration file";
154 msg += " ";
155 msg += filename;
156 msg += " unrecognized section: " + sectionName;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700157 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700158 }
159 }
160}
161
162void
163ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
164 const std::string& filename)
165{
166 using namespace ndn::security::conf;
167
168 ConfigSection::const_iterator propertyIt = configSection.begin();
169
170 // Get rule.id
171 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700172 BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700173
174 std::string ruleId = propertyIt->second.data();
175 propertyIt++;
176
177 // Get rule.for
178 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700179 BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700180
181 std::string usage = propertyIt->second.data();
182 propertyIt++;
183
184 bool isForData;
185 if (boost::iequals(usage, "data"))
186 isForData = true;
187 else if (boost::iequals(usage, "interest"))
188 isForData = false;
189 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700190 BOOST_THROW_EXCEPTION(Error("Unrecognized <rule.for>: " + usage
191 + " in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700192
193 // Get rule.filter(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700194 std::vector<shared_ptr<Filter>> filters;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700195 for (; propertyIt != configSection.end(); propertyIt++)
196 {
197 if (!boost::iequals(propertyIt->first, "filter"))
198 {
199 if (boost::iequals(propertyIt->first, "checker"))
200 break;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700201 BOOST_THROW_EXCEPTION(Error("Expect <rule.filter> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700202 }
203
204 filters.push_back(FilterFactory::create(propertyIt->second));
205 continue;
206 }
207
208 // Get rule.checker(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700209 std::vector<shared_ptr<Checker>> checkers;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700210 for (; propertyIt != configSection.end(); propertyIt++)
211 {
212 if (!boost::iequals(propertyIt->first, "checker"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700213 BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700214
215 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
216 continue;
217 }
218
219 // Check other stuff
220 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700221 BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700222
223 if (checkers.size() == 0)
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700224 BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700225
226 if (isForData)
227 {
228 shared_ptr<DataRule> rule(new DataRule(ruleId));
229 for (size_t i = 0; i < filters.size(); i++)
230 rule->addFilter(filters[i]);
231 for (size_t i = 0; i < checkers.size(); i++)
232 rule->addChecker(checkers[i]);
233
234 m_dataRules.push_back(rule);
235 }
236 else
237 {
238 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
239 for (size_t i = 0; i < filters.size(); i++)
240 rule->addFilter(filters[i]);
241 for (size_t i = 0; i < checkers.size(); i++)
242 rule->addChecker(checkers[i]);
243
244 m_interestRules.push_back(rule);
245 }
246}
247
248void
249ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
250 const std::string& filename)
251{
252 using namespace ndn::security::conf;
253 using namespace boost::filesystem;
254
255 ConfigSection::const_iterator propertyIt = configSection.begin();
256
257 // Get trust-anchor.type
258 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700259 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.type>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700260
261 std::string type = propertyIt->second.data();
262 propertyIt++;
263
264 if (boost::iequals(type, "file"))
265 {
266 // Get trust-anchor.file
267 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700268 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700269
270 std::string file = propertyIt->second.data();
271 propertyIt++;
272
273 // Check other stuff
274 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700275 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700276
277 path certfilePath = absolute(file, path(filename).parent_path());
278 shared_ptr<IdentityCertificate> idCert =
279 io::load<IdentityCertificate>(certfilePath.string());
280
281 if (static_cast<bool>(idCert))
282 {
283 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700284 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700285 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
286 }
287 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700288 BOOST_THROW_EXCEPTION(Error("Cannot read certificate from file: " +
289 certfilePath.native()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700290
291 return;
292 }
293 else if (boost::iequals(type, "base64"))
294 {
295 // Get trust-anchor.base64-string
296 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700297 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700298
299 std::stringstream ss(propertyIt->second.data());
300 propertyIt++;
301
302 // Check other stuff
303 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700304 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700305
306 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
307
308 if (static_cast<bool>(idCert))
309 {
310 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700311 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700312 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
313 }
314 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700315 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700316
317 return;
318 }
Yingdi Yub4650652014-04-17 10:19:59 -0700319 else if (boost::iequals(type, "dir"))
320 {
321 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700322 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
Yingdi Yub4650652014-04-17 10:19:59 -0700323
324 std::string dirString(propertyIt->second.data());
325 propertyIt++;
326
327 if (propertyIt != configSection.end())
328 {
329 if (boost::iequals(propertyIt->first, "refresh"))
330 {
331 using namespace boost::filesystem;
332
333 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
334 propertyIt++;
335
336 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700337 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor"));
Yingdi Yub4650652014-04-17 10:19:59 -0700338
339 path dirPath = absolute(dirString, path(filename).parent_path());
340
341 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
342
343 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
344
345 return;
346 }
347 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700348 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.refresh>!"));
Yingdi Yub4650652014-04-17 10:19:59 -0700349 }
350 else
351 {
352 using namespace boost::filesystem;
353
354 path dirPath = absolute(dirString, path(filename).parent_path());
355
356 directory_iterator end;
357
358 for (directory_iterator it(dirPath); it != end; it++)
359 {
360 shared_ptr<IdentityCertificate> idCert =
361 io::load<IdentityCertificate>(it->path().string());
362
363 if (static_cast<bool>(idCert))
364 m_staticContainer.add(idCert);
365 }
366
367 return;
368 }
369 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700370 else if (boost::iequals(type, "any"))
371 {
372 m_shouldValidate = false;
373 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700374 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700375 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700376}
377
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800378void
379ValidatorConfig::reset()
380{
381 if (static_cast<bool>(m_certificateCache))
382 m_certificateCache->reset();
383 m_interestRules.clear();
384 m_dataRules.clear();
385
386 m_anchors.clear();
387
388 m_staticContainer = TrustAnchorContainer();
389
390 m_dynamicContainers.clear();
391}
392
393bool
394ValidatorConfig::isEmpty()
395{
396 if ((!static_cast<bool>(m_certificateCache) || m_certificateCache->isEmpty()) &&
397 m_interestRules.empty() &&
398 m_dataRules.empty() &&
399 m_anchors.empty())
400 return true;
401 return false;
402}
403
Yingdi Yub4650652014-04-17 10:19:59 -0700404time::nanoseconds
405ValidatorConfig::getRefreshPeriod(std::string inputString)
406{
407 char unit = inputString[inputString.size() - 1];
408 std::string refreshString = inputString.substr(0, inputString.size() - 1);
409
410 uint32_t number;
411
412 try
413 {
414 number = boost::lexical_cast<uint32_t>(refreshString);
415 }
416 catch (boost::bad_lexical_cast&)
417 {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700418 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
Yingdi Yub4650652014-04-17 10:19:59 -0700419 }
420
421 if (number == 0)
422 return getDefaultRefreshPeriod();
423
424 switch (unit)
425 {
426 case 'h':
427 return time::duration_cast<time::nanoseconds>(time::hours(number));
428 case 'm':
429 return time::duration_cast<time::nanoseconds>(time::minutes(number));
430 case 's':
431 return time::duration_cast<time::nanoseconds>(time::seconds(number));
432 default:
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700433 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
Yingdi Yub4650652014-04-17 10:19:59 -0700434 }
435}
436
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800437time::nanoseconds
438ValidatorConfig::getDefaultRefreshPeriod()
439{
440 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
441}
442
Yingdi Yub4650652014-04-17 10:19:59 -0700443void
444ValidatorConfig::refreshAnchors()
445{
446 time::system_clock::TimePoint now = time::system_clock::now();
447
448 bool isRefreshed = false;
449
450 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
451 cIt != m_dynamicContainers.end(); cIt++)
452 {
453 if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now)
454 {
455 isRefreshed = true;
456 cIt->refresh();
457 cIt->setLastRefresh(now);
458 }
459 else
460 break;
461 }
462
463 if (isRefreshed)
464 {
465 m_anchors.clear();
466
467 for (CertificateList::const_iterator it = m_staticContainer.getAll().begin();
468 it != m_staticContainer.getAll().end(); it++)
469 {
470 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
471 }
472
473 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
474 cIt != m_dynamicContainers.end(); cIt++)
475 {
476 const CertificateList& certList = cIt->getAll();
477
478 for (CertificateList::const_iterator it = certList.begin();
479 it != certList.end(); it++)
480 {
481 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
482 }
483 }
484 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
485 }
486}
487
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700488void
489ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700490 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700491 const OnDataValidated& onValidated,
492 const OnDataValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700493 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700494{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700495 if (!m_shouldValidate)
496 return onValidated(data.shared_from_this());
497
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700498 bool isMatched = false;
499 int8_t checkResult = -1;
500
501 for (DataRuleList::iterator it = m_dataRules.begin();
502 it != m_dataRules.end(); it++)
503 {
504 if ((*it)->match(data))
505 {
506 isMatched = true;
507 checkResult = (*it)->check(data, onValidated, onValidationFailed);
508 break;
509 }
510 }
511
512 if (!isMatched)
513 return onValidationFailed(data.shared_from_this(), "No rule matched!");
514
515 if (checkResult == 0)
516 {
517 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700518 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700519 onValidated, onValidationFailed, nextSteps);
520 }
521}
522
523void
524ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700525 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700526 const OnInterestValidated& onValidated,
527 const OnInterestValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700528 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700529{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700530 if (!m_shouldValidate)
531 return onValidated(interest.shared_from_this());
532
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700533 // If interestName has less than 4 name components,
534 // it is definitely not a signed interest.
535 if (interest.getName().size() < signed_interest::MIN_LENGTH)
536 return onValidationFailed(interest.shared_from_this(),
537 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700538
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700539 try
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700540 {
541 const Name& interestName = interest.getName();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700542 Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(),
543 interestName[signed_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700544
Yingdi Yu4a557052014-07-09 16:40:37 -0700545 if (!signature.hasKeyLocator())
546 return onValidationFailed(interest.shared_from_this(),
547 "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700548
Yingdi Yu4a557052014-07-09 16:40:37 -0700549 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700550
551 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
552 return onValidationFailed(interest.shared_from_this(),
553 "Key Locator is not a name");
554
555 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
556
557 bool isMatched = false;
558 int8_t checkResult = -1;
559
560 for (InterestRuleList::iterator it = m_interestRules.begin();
561 it != m_interestRules.end(); it++)
562 {
563 if ((*it)->match(interest))
564 {
565 isMatched = true;
566 checkResult = (*it)->check(interest,
567 bind(&ValidatorConfig::checkTimestamp, this, _1,
568 keyName, onValidated, onValidationFailed),
569 onValidationFailed);
570 break;
571 }
572 }
573
574 if (!isMatched)
575 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
576
577 if (checkResult == 0)
578 {
579 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
Yingdi Yu4a557052014-07-09 16:40:37 -0700580 (interest, signature, nSteps,
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700581 bind(&ValidatorConfig::checkTimestamp, this, _1,
582 keyName, onValidated, onValidationFailed),
583 onValidationFailed,
584 nextSteps);
585 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700586 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700587 catch (Signature::Error& e)
588 {
589 return onValidationFailed(interest.shared_from_this(),
590 "No valid signature");
591 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700592 catch (KeyLocator::Error& e)
593 {
594 return onValidationFailed(interest.shared_from_this(),
595 "No valid KeyLocator");
596 }
597 catch (IdentityCertificate::Error& e)
598 {
599 return onValidationFailed(interest.shared_from_this(),
600 "Cannot determine the signing key");
601 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800602
603 catch (tlv::Error& e)
604 {
605 return onValidationFailed(interest.shared_from_this(),
606 "Cannot decode signature");
607 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700608}
609
610void
611ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
612 const Name& keyName,
613 const OnInterestValidated& onValidated,
614 const OnInterestValidationFailed& onValidationFailed)
615{
616 const Name& interestName = interest->getName();
617 time::system_clock::TimePoint interestTime;
618
619 try
620 {
621 interestTime =
622 time::fromUnixTimestamp(
623 time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber()));
624 }
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600625 catch (tlv::Error& e)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700626 {
627 return onValidationFailed(interest,
628 "Cannot decode signature related TLVs");
629 }
630
631 time::system_clock::TimePoint currentTime = time::system_clock::now();
632
633 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
634 if (timestampIt == m_lastTimestamp.end())
635 {
636 if (!(currentTime - m_graceInterval <= interestTime &&
637 interestTime <= currentTime + m_graceInterval))
638 return onValidationFailed(interest,
639 "The command is not in grace interval: " +
640 interest->getName().toUri());
641 }
642 else
643 {
644 if (interestTime <= timestampIt->second)
645 return onValidationFailed(interest,
646 "The command is outdated: " +
647 interest->getName().toUri());
648 }
649
650 //Update timestamp
651 if (timestampIt == m_lastTimestamp.end())
652 {
653 cleanOldKeys();
654 m_lastTimestamp[keyName] = interestTime;
655 }
656 else
657 {
658 timestampIt->second = interestTime;
659 }
660
661 return onValidated(interest);
662}
663
664void
665ValidatorConfig::cleanOldKeys()
666{
667 if (m_lastTimestamp.size() < m_maxTrackedKeys)
668 return;
669
670 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
671 LastTimestampMap::iterator end = m_lastTimestamp.end();
672
673 time::system_clock::TimePoint now = time::system_clock::now();
674 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
675 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
676
677 while (timestampIt != end)
678 {
679 if (now - timestampIt->second > m_keyTimestampTtl)
680 {
681 LastTimestampMap::iterator toDelete = timestampIt;
682 timestampIt++;
683 m_lastTimestamp.erase(toDelete);
684 continue;
685 }
686
687 if (timestampIt->second < oldestTimestamp)
688 {
689 oldestTimestamp = timestampIt->second;
690 oldestKeyIt = timestampIt;
691 }
692
693 timestampIt++;
694 }
695
696 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
697 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700698}
699
Yingdi Yub4650652014-04-17 10:19:59 -0700700void
701ValidatorConfig::DynamicTrustAnchorContainer::refresh()
702{
703 using namespace boost::filesystem;
704
705 m_certificates.clear();
706
707 if (m_isDir)
708 {
709 directory_iterator end;
710
711 for (directory_iterator it(m_path); it != end; it++)
712 {
713 shared_ptr<IdentityCertificate> idCert =
714 io::load<IdentityCertificate>(it->path().string());
715
716 if (static_cast<bool>(idCert))
717 m_certificates.push_back(idCert);
718 }
719 }
720 else
721 {
722 shared_ptr<IdentityCertificate> idCert =
723 io::load<IdentityCertificate>(m_path.string());
724
725 if (static_cast<bool>(idCert))
726 m_certificates.push_back(idCert);
727 }
728}
729
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800730template<class Packet, class OnValidated, class OnFailed>
731void
732ValidatorConfig::checkSignature(const Packet& packet,
733 const Signature& signature,
734 size_t nSteps,
735 const OnValidated& onValidated,
736 const OnFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700737 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800738{
739 if (signature.getType() == tlv::DigestSha256)
740 {
741 DigestSha256 sigSha256(signature);
742
743 if (verifySignature(packet, sigSha256))
744 return onValidated(packet.shared_from_this());
745 else
746 return onValidationFailed(packet.shared_from_this(),
747 "Sha256 Signature cannot be verified!");
748 }
749
750 try {
751 switch (signature.getType()) {
752 case tlv::SignatureSha256WithRsa:
753 case tlv::SignatureSha256WithEcdsa:
754 {
755 if (!signature.hasKeyLocator()) {
756 return onValidationFailed(packet.shared_from_this(),
757 "Missing KeyLocator in SignatureInfo");
758 }
759 break;
760 }
761 default:
762 return onValidationFailed(packet.shared_from_this(),
763 "Unsupported signature type");
764 }
765 }
766 catch (KeyLocator::Error& e) {
767 return onValidationFailed(packet.shared_from_this(),
768 "Cannot decode KeyLocator in public key signature");
769 }
770 catch (tlv::Error& e) {
771 return onValidationFailed(packet.shared_from_this(),
772 "Cannot decode public key signature");
773 }
774
775
776 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
777 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
778 }
779
780 const Name& keyLocatorName = signature.getKeyLocator().getName();
781
782 shared_ptr<const Certificate> trustedCert;
783
784 refreshAnchors();
785
786 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
787 if (m_anchors.end() == it && static_cast<bool>(m_certificateCache))
788 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700789 else if (m_anchors.end() != it)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800790 trustedCert = it->second;
791
792 if (static_cast<bool>(trustedCert))
793 {
794 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
795 return onValidated(packet.shared_from_this());
796 else
797 return onValidationFailed(packet.shared_from_this(),
798 "Cannot verify signature");
799 }
800 else
801 {
802 if (m_stepLimit == nSteps)
803 return onValidationFailed(packet.shared_from_this(),
804 "Maximum steps of validation reached");
805
806 OnDataValidated onCertValidated =
807 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
808 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
809
810 OnDataValidationFailed onCertValidationFailed =
811 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
812 this, _1, _2, packet.shared_from_this(), onValidationFailed);
813
814 Interest certInterest(keyLocatorName);
815
816 shared_ptr<ValidationRequest> nextStep =
817 make_shared<ValidationRequest>(certInterest,
818 onCertValidated,
819 onCertValidationFailed,
820 1, nSteps + 1);
821
822 nextSteps.push_back(nextStep);
823 return;
824 }
825
826 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
827}
828
829template<class Packet, class OnValidated, class OnFailed>
830void
831ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
832 const shared_ptr<const Packet>& packet,
833 const OnValidated& onValidated,
834 const OnFailed& onValidationFailed)
835{
Yingdi Yu80979ba2014-11-25 14:38:36 -0800836 if (signCertificate->getContentType() != tlv::ContentType_Key)
837 return onValidationFailed(packet,
838 "Cannot retrieve signer's cert: " +
839 signCertificate->getName().toUri());
840
841 shared_ptr<IdentityCertificate> certificate;
842 try {
843 certificate = make_shared<IdentityCertificate>(*signCertificate);
844 }
845 catch (tlv::Error&) {
846 return onValidationFailed(packet,
847 "Cannot decode signer's cert: " +
848 signCertificate->getName().toUri());
849 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800850
851 if (!certificate->isTooLate() && !certificate->isTooEarly())
852 {
853 if (static_cast<bool>(m_certificateCache))
854 m_certificateCache->insertCertificate(certificate);
855
856 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
857 return onValidated(packet);
858 else
859 return onValidationFailed(packet,
860 "Cannot verify signature: " +
861 packet->getName().toUri());
862 }
863 else
864 {
865 return onValidationFailed(packet,
866 "Signing certificate " +
867 signCertificate->getName().toUri() +
868 " is no longer valid.");
869 }
870}
871
872template<class Packet, class OnFailed>
873void
874ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
875 const std::string& failureInfo,
876 const shared_ptr<const Packet>& packet,
877 const OnFailed& onValidationFailed)
878{
879 onValidationFailed(packet, failureInfo);
880}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700881
882} // namespace ndn