blob: c9dbf4d75267d63ae2fa3a2b09e0b393bec276de [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;
Yingdi Yu0f5fb692014-06-10 12:07:28 -070035const time::milliseconds ValidatorConfig::DEFAULT_GRACE_INTERVAL(3000);
36const time::system_clock::Duration ValidatorConfig::DEFAULT_KEY_TIMESTAMP_TTL = time::hours(1);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070037
Yingdi Yu4e9b0692014-11-04 16:13:56 -080038ValidatorConfig::ValidatorConfig(Face* face,
39 const shared_ptr<CertificateCache>& certificateCache,
40 const time::milliseconds& graceInterval,
41 const size_t stepLimit,
42 const size_t maxTrackedKeys,
43 const time::system_clock::Duration& keyTimestampTtl)
44 : Validator(face)
45 , m_shouldValidate(true)
46 , m_stepLimit(stepLimit)
47 , m_certificateCache(certificateCache)
48 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
49 DEFAULT_GRACE_INTERVAL : graceInterval)
50 , m_maxTrackedKeys(maxTrackedKeys)
51 , m_keyTimestampTtl(keyTimestampTtl)
52{
53 if (!static_cast<bool>(m_certificateCache) && face != nullptr)
54 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->getIoService()));
55}
56
Yingdi Yu96e64062014-04-15 19:57:33 -070057ValidatorConfig::ValidatorConfig(Face& face,
58 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu0f5fb692014-06-10 12:07:28 -070059 const time::milliseconds& graceInterval,
60 const size_t stepLimit,
61 const size_t maxTrackedKeys,
62 const time::system_clock::Duration& keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070063 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070064 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070065 , m_stepLimit(stepLimit)
66 , m_certificateCache(certificateCache)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070067 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
68 DEFAULT_GRACE_INTERVAL : graceInterval)
69 , m_maxTrackedKeys(maxTrackedKeys)
70 , m_keyTimestampTtl(keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070071{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070072 if (!static_cast<bool>(m_certificateCache))
Yingdi Yu4e9b0692014-11-04 16:13:56 -080073 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070074}
75
76void
77ValidatorConfig::load(const std::string& filename)
78{
79 std::ifstream inputFile;
80 inputFile.open(filename.c_str());
81 if (!inputFile.good() || !inputFile.is_open())
82 {
83 std::string msg = "Failed to read configuration file: ";
84 msg += filename;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070085 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070086 }
87 load(inputFile, filename);
88 inputFile.close();
89}
90
91void
92ValidatorConfig::load(const std::string& input, const std::string& filename)
93{
94 std::istringstream inputStream(input);
95 load(inputStream, filename);
96}
97
98
99void
100ValidatorConfig::load(std::istream& input, const std::string& filename)
101{
102 security::conf::ConfigSection tree;
103 try
104 {
105 boost::property_tree::read_info(input, tree);
106 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700107 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700108 {
109 std::stringstream msg;
110 msg << "Failed to parse configuration file";
111 msg << " " << filename;
112 msg << " " << error.message() << " line " << error.line();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700113 BOOST_THROW_EXCEPTION(security::conf::Error(msg.str()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700114 }
115
Yingdi Yudfa9d732014-04-09 09:53:01 -0700116 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700117}
118
119void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700120ValidatorConfig::load(const security::conf::ConfigSection& configSection,
121 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700122{
123 BOOST_ASSERT(!filename.empty());
124
Yingdi Yu58f33712014-04-16 16:57:47 -0700125 reset();
126
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700127 if (configSection.begin() == configSection.end())
128 {
129 std::string msg = "Error processing configuration file";
130 msg += ": ";
131 msg += filename;
132 msg += " no data";
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700133 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700134 }
135
136 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
137 i != configSection.end(); ++i)
138 {
139 const std::string& sectionName = i->first;
140 const security::conf::ConfigSection& section = i->second;
141
142 if (boost::iequals(sectionName, "rule"))
143 {
144 onConfigRule(section, filename);
145 }
146 else if (boost::iequals(sectionName, "trust-anchor"))
147 {
148 onConfigTrustAnchor(section, filename);
149 }
150 else
151 {
152 std::string msg = "Error processing configuration file";
153 msg += " ";
154 msg += filename;
155 msg += " unrecognized section: " + sectionName;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700156 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700157 }
158 }
159}
160
161void
162ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
163 const std::string& filename)
164{
165 using namespace ndn::security::conf;
166
167 ConfigSection::const_iterator propertyIt = configSection.begin();
168
169 // Get rule.id
170 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700171 BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700172
173 std::string ruleId = propertyIt->second.data();
174 propertyIt++;
175
176 // Get rule.for
177 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700178 BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700179
180 std::string usage = propertyIt->second.data();
181 propertyIt++;
182
183 bool isForData;
184 if (boost::iequals(usage, "data"))
185 isForData = true;
186 else if (boost::iequals(usage, "interest"))
187 isForData = false;
188 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700189 BOOST_THROW_EXCEPTION(Error("Unrecognized <rule.for>: " + usage
190 + " in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700191
192 // Get rule.filter(s)
193 std::vector<shared_ptr<Filter> > filters;
194 for (; propertyIt != configSection.end(); propertyIt++)
195 {
196 if (!boost::iequals(propertyIt->first, "filter"))
197 {
198 if (boost::iequals(propertyIt->first, "checker"))
199 break;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700200 BOOST_THROW_EXCEPTION(Error("Expect <rule.filter> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700201 }
202
203 filters.push_back(FilterFactory::create(propertyIt->second));
204 continue;
205 }
206
207 // Get rule.checker(s)
208 std::vector<shared_ptr<Checker> > checkers;
209 for (; propertyIt != configSection.end(); propertyIt++)
210 {
211 if (!boost::iequals(propertyIt->first, "checker"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700212 BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700213
214 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
215 continue;
216 }
217
218 // Check other stuff
219 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700220 BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700221
222 if (checkers.size() == 0)
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700223 BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700224
225 if (isForData)
226 {
227 shared_ptr<DataRule> rule(new DataRule(ruleId));
228 for (size_t i = 0; i < filters.size(); i++)
229 rule->addFilter(filters[i]);
230 for (size_t i = 0; i < checkers.size(); i++)
231 rule->addChecker(checkers[i]);
232
233 m_dataRules.push_back(rule);
234 }
235 else
236 {
237 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
238 for (size_t i = 0; i < filters.size(); i++)
239 rule->addFilter(filters[i]);
240 for (size_t i = 0; i < checkers.size(); i++)
241 rule->addChecker(checkers[i]);
242
243 m_interestRules.push_back(rule);
244 }
245}
246
247void
248ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
249 const std::string& filename)
250{
251 using namespace ndn::security::conf;
252 using namespace boost::filesystem;
253
254 ConfigSection::const_iterator propertyIt = configSection.begin();
255
256 // Get trust-anchor.type
257 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700258 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.type>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700259
260 std::string type = propertyIt->second.data();
261 propertyIt++;
262
263 if (boost::iequals(type, "file"))
264 {
265 // Get trust-anchor.file
266 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700267 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700268
269 std::string file = propertyIt->second.data();
270 propertyIt++;
271
272 // Check other stuff
273 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700274 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700275
276 path certfilePath = absolute(file, path(filename).parent_path());
277 shared_ptr<IdentityCertificate> idCert =
278 io::load<IdentityCertificate>(certfilePath.string());
279
280 if (static_cast<bool>(idCert))
281 {
282 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700283 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700284 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
285 }
286 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700287 BOOST_THROW_EXCEPTION(Error("Cannot read certificate from file: " +
288 certfilePath.native()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700289
290 return;
291 }
292 else if (boost::iequals(type, "base64"))
293 {
294 // Get trust-anchor.base64-string
295 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700296 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700297
298 std::stringstream ss(propertyIt->second.data());
299 propertyIt++;
300
301 // Check other stuff
302 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700303 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700304
305 shared_ptr<IdentityCertificate> idCert = io::load<IdentityCertificate>(ss);
306
307 if (static_cast<bool>(idCert))
308 {
309 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700310 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700311 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
312 }
313 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700314 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700315
316 return;
317 }
Yingdi Yub4650652014-04-17 10:19:59 -0700318 else if (boost::iequals(type, "dir"))
319 {
320 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700321 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
Yingdi Yub4650652014-04-17 10:19:59 -0700322
323 std::string dirString(propertyIt->second.data());
324 propertyIt++;
325
326 if (propertyIt != configSection.end())
327 {
328 if (boost::iequals(propertyIt->first, "refresh"))
329 {
330 using namespace boost::filesystem;
331
332 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
333 propertyIt++;
334
335 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700336 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor"));
Yingdi Yub4650652014-04-17 10:19:59 -0700337
338 path dirPath = absolute(dirString, path(filename).parent_path());
339
340 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
341
342 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
343
344 return;
345 }
346 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700347 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.refresh>!"));
Yingdi Yub4650652014-04-17 10:19:59 -0700348 }
349 else
350 {
351 using namespace boost::filesystem;
352
353 path dirPath = absolute(dirString, path(filename).parent_path());
354
355 directory_iterator end;
356
357 for (directory_iterator it(dirPath); it != end; it++)
358 {
359 shared_ptr<IdentityCertificate> idCert =
360 io::load<IdentityCertificate>(it->path().string());
361
362 if (static_cast<bool>(idCert))
363 m_staticContainer.add(idCert);
364 }
365
366 return;
367 }
368 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700369 else if (boost::iequals(type, "any"))
370 {
371 m_shouldValidate = false;
372 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700373 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700374 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700375}
376
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800377void
378ValidatorConfig::reset()
379{
380 if (static_cast<bool>(m_certificateCache))
381 m_certificateCache->reset();
382 m_interestRules.clear();
383 m_dataRules.clear();
384
385 m_anchors.clear();
386
387 m_staticContainer = TrustAnchorContainer();
388
389 m_dynamicContainers.clear();
390}
391
392bool
393ValidatorConfig::isEmpty()
394{
395 if ((!static_cast<bool>(m_certificateCache) || m_certificateCache->isEmpty()) &&
396 m_interestRules.empty() &&
397 m_dataRules.empty() &&
398 m_anchors.empty())
399 return true;
400 return false;
401}
402
Yingdi Yub4650652014-04-17 10:19:59 -0700403time::nanoseconds
404ValidatorConfig::getRefreshPeriod(std::string inputString)
405{
406 char unit = inputString[inputString.size() - 1];
407 std::string refreshString = inputString.substr(0, inputString.size() - 1);
408
409 uint32_t number;
410
411 try
412 {
413 number = boost::lexical_cast<uint32_t>(refreshString);
414 }
415 catch (boost::bad_lexical_cast&)
416 {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700417 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
Yingdi Yub4650652014-04-17 10:19:59 -0700418 }
419
420 if (number == 0)
421 return getDefaultRefreshPeriod();
422
423 switch (unit)
424 {
425 case 'h':
426 return time::duration_cast<time::nanoseconds>(time::hours(number));
427 case 'm':
428 return time::duration_cast<time::nanoseconds>(time::minutes(number));
429 case 's':
430 return time::duration_cast<time::nanoseconds>(time::seconds(number));
431 default:
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700432 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
Yingdi Yub4650652014-04-17 10:19:59 -0700433 }
434}
435
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800436time::nanoseconds
437ValidatorConfig::getDefaultRefreshPeriod()
438{
439 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
440}
441
Yingdi Yub4650652014-04-17 10:19:59 -0700442void
443ValidatorConfig::refreshAnchors()
444{
445 time::system_clock::TimePoint now = time::system_clock::now();
446
447 bool isRefreshed = false;
448
449 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
450 cIt != m_dynamicContainers.end(); cIt++)
451 {
452 if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now)
453 {
454 isRefreshed = true;
455 cIt->refresh();
456 cIt->setLastRefresh(now);
457 }
458 else
459 break;
460 }
461
462 if (isRefreshed)
463 {
464 m_anchors.clear();
465
466 for (CertificateList::const_iterator it = m_staticContainer.getAll().begin();
467 it != m_staticContainer.getAll().end(); it++)
468 {
469 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
470 }
471
472 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
473 cIt != m_dynamicContainers.end(); cIt++)
474 {
475 const CertificateList& certList = cIt->getAll();
476
477 for (CertificateList::const_iterator it = certList.begin();
478 it != certList.end(); it++)
479 {
480 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
481 }
482 }
483 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
484 }
485}
486
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700487void
488ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700489 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700490 const OnDataValidated& onValidated,
491 const OnDataValidationFailed& onValidationFailed,
492 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
493{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700494 if (!m_shouldValidate)
495 return onValidated(data.shared_from_this());
496
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700497 bool isMatched = false;
498 int8_t checkResult = -1;
499
500 for (DataRuleList::iterator it = m_dataRules.begin();
501 it != m_dataRules.end(); it++)
502 {
503 if ((*it)->match(data))
504 {
505 isMatched = true;
506 checkResult = (*it)->check(data, onValidated, onValidationFailed);
507 break;
508 }
509 }
510
511 if (!isMatched)
512 return onValidationFailed(data.shared_from_this(), "No rule matched!");
513
514 if (checkResult == 0)
515 {
516 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700517 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700518 onValidated, onValidationFailed, nextSteps);
519 }
520}
521
522void
523ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700524 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700525 const OnInterestValidated& onValidated,
526 const OnInterestValidationFailed& onValidationFailed,
527 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
528{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700529 if (!m_shouldValidate)
530 return onValidated(interest.shared_from_this());
531
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700532 // If interestName has less than 4 name components,
533 // it is definitely not a signed interest.
534 if (interest.getName().size() < signed_interest::MIN_LENGTH)
535 return onValidationFailed(interest.shared_from_this(),
536 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700537
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700538 try
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700539 {
540 const Name& interestName = interest.getName();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700541 Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(),
542 interestName[signed_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700543
Yingdi Yu4a557052014-07-09 16:40:37 -0700544 if (!signature.hasKeyLocator())
545 return onValidationFailed(interest.shared_from_this(),
546 "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700547
Yingdi Yu4a557052014-07-09 16:40:37 -0700548 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700549
550 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
551 return onValidationFailed(interest.shared_from_this(),
552 "Key Locator is not a name");
553
554 Name keyName = IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
555
556 bool isMatched = false;
557 int8_t checkResult = -1;
558
559 for (InterestRuleList::iterator it = m_interestRules.begin();
560 it != m_interestRules.end(); it++)
561 {
562 if ((*it)->match(interest))
563 {
564 isMatched = true;
565 checkResult = (*it)->check(interest,
566 bind(&ValidatorConfig::checkTimestamp, this, _1,
567 keyName, onValidated, onValidationFailed),
568 onValidationFailed);
569 break;
570 }
571 }
572
573 if (!isMatched)
574 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
575
576 if (checkResult == 0)
577 {
578 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
Yingdi Yu4a557052014-07-09 16:40:37 -0700579 (interest, signature, nSteps,
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700580 bind(&ValidatorConfig::checkTimestamp, this, _1,
581 keyName, onValidated, onValidationFailed),
582 onValidationFailed,
583 nextSteps);
584 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700585 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700586 catch (Signature::Error& e)
587 {
588 return onValidationFailed(interest.shared_from_this(),
589 "No valid signature");
590 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700591 catch (KeyLocator::Error& e)
592 {
593 return onValidationFailed(interest.shared_from_this(),
594 "No valid KeyLocator");
595 }
596 catch (IdentityCertificate::Error& e)
597 {
598 return onValidationFailed(interest.shared_from_this(),
599 "Cannot determine the signing key");
600 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800601
602 catch (tlv::Error& e)
603 {
604 return onValidationFailed(interest.shared_from_this(),
605 "Cannot decode signature");
606 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700607}
608
609void
610ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
611 const Name& keyName,
612 const OnInterestValidated& onValidated,
613 const OnInterestValidationFailed& onValidationFailed)
614{
615 const Name& interestName = interest->getName();
616 time::system_clock::TimePoint interestTime;
617
618 try
619 {
620 interestTime =
621 time::fromUnixTimestamp(
622 time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber()));
623 }
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600624 catch (tlv::Error& e)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700625 {
626 return onValidationFailed(interest,
627 "Cannot decode signature related TLVs");
628 }
629
630 time::system_clock::TimePoint currentTime = time::system_clock::now();
631
632 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
633 if (timestampIt == m_lastTimestamp.end())
634 {
635 if (!(currentTime - m_graceInterval <= interestTime &&
636 interestTime <= currentTime + m_graceInterval))
637 return onValidationFailed(interest,
638 "The command is not in grace interval: " +
639 interest->getName().toUri());
640 }
641 else
642 {
643 if (interestTime <= timestampIt->second)
644 return onValidationFailed(interest,
645 "The command is outdated: " +
646 interest->getName().toUri());
647 }
648
649 //Update timestamp
650 if (timestampIt == m_lastTimestamp.end())
651 {
652 cleanOldKeys();
653 m_lastTimestamp[keyName] = interestTime;
654 }
655 else
656 {
657 timestampIt->second = interestTime;
658 }
659
660 return onValidated(interest);
661}
662
663void
664ValidatorConfig::cleanOldKeys()
665{
666 if (m_lastTimestamp.size() < m_maxTrackedKeys)
667 return;
668
669 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
670 LastTimestampMap::iterator end = m_lastTimestamp.end();
671
672 time::system_clock::TimePoint now = time::system_clock::now();
673 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
674 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
675
676 while (timestampIt != end)
677 {
678 if (now - timestampIt->second > m_keyTimestampTtl)
679 {
680 LastTimestampMap::iterator toDelete = timestampIt;
681 timestampIt++;
682 m_lastTimestamp.erase(toDelete);
683 continue;
684 }
685
686 if (timestampIt->second < oldestTimestamp)
687 {
688 oldestTimestamp = timestampIt->second;
689 oldestKeyIt = timestampIt;
690 }
691
692 timestampIt++;
693 }
694
695 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
696 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700697}
698
Yingdi Yub4650652014-04-17 10:19:59 -0700699void
700ValidatorConfig::DynamicTrustAnchorContainer::refresh()
701{
702 using namespace boost::filesystem;
703
704 m_certificates.clear();
705
706 if (m_isDir)
707 {
708 directory_iterator end;
709
710 for (directory_iterator it(m_path); it != end; it++)
711 {
712 shared_ptr<IdentityCertificate> idCert =
713 io::load<IdentityCertificate>(it->path().string());
714
715 if (static_cast<bool>(idCert))
716 m_certificates.push_back(idCert);
717 }
718 }
719 else
720 {
721 shared_ptr<IdentityCertificate> idCert =
722 io::load<IdentityCertificate>(m_path.string());
723
724 if (static_cast<bool>(idCert))
725 m_certificates.push_back(idCert);
726 }
727}
728
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800729template<class Packet, class OnValidated, class OnFailed>
730void
731ValidatorConfig::checkSignature(const Packet& packet,
732 const Signature& signature,
733 size_t nSteps,
734 const OnValidated& onValidated,
735 const OnFailed& onValidationFailed,
736 std::vector<shared_ptr<ValidationRequest> >& nextSteps)
737{
738 if (signature.getType() == tlv::DigestSha256)
739 {
740 DigestSha256 sigSha256(signature);
741
742 if (verifySignature(packet, sigSha256))
743 return onValidated(packet.shared_from_this());
744 else
745 return onValidationFailed(packet.shared_from_this(),
746 "Sha256 Signature cannot be verified!");
747 }
748
749 try {
750 switch (signature.getType()) {
751 case tlv::SignatureSha256WithRsa:
752 case tlv::SignatureSha256WithEcdsa:
753 {
754 if (!signature.hasKeyLocator()) {
755 return onValidationFailed(packet.shared_from_this(),
756 "Missing KeyLocator in SignatureInfo");
757 }
758 break;
759 }
760 default:
761 return onValidationFailed(packet.shared_from_this(),
762 "Unsupported signature type");
763 }
764 }
765 catch (KeyLocator::Error& e) {
766 return onValidationFailed(packet.shared_from_this(),
767 "Cannot decode KeyLocator in public key signature");
768 }
769 catch (tlv::Error& e) {
770 return onValidationFailed(packet.shared_from_this(),
771 "Cannot decode public key signature");
772 }
773
774
775 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
776 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
777 }
778
779 const Name& keyLocatorName = signature.getKeyLocator().getName();
780
781 shared_ptr<const Certificate> trustedCert;
782
783 refreshAnchors();
784
785 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
786 if (m_anchors.end() == it && static_cast<bool>(m_certificateCache))
787 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
788 else
789 trustedCert = it->second;
790
791 if (static_cast<bool>(trustedCert))
792 {
793 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
794 return onValidated(packet.shared_from_this());
795 else
796 return onValidationFailed(packet.shared_from_this(),
797 "Cannot verify signature");
798 }
799 else
800 {
801 if (m_stepLimit == nSteps)
802 return onValidationFailed(packet.shared_from_this(),
803 "Maximum steps of validation reached");
804
805 OnDataValidated onCertValidated =
806 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
807 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
808
809 OnDataValidationFailed onCertValidationFailed =
810 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
811 this, _1, _2, packet.shared_from_this(), onValidationFailed);
812
813 Interest certInterest(keyLocatorName);
814
815 shared_ptr<ValidationRequest> nextStep =
816 make_shared<ValidationRequest>(certInterest,
817 onCertValidated,
818 onCertValidationFailed,
819 1, nSteps + 1);
820
821 nextSteps.push_back(nextStep);
822 return;
823 }
824
825 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
826}
827
828template<class Packet, class OnValidated, class OnFailed>
829void
830ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
831 const shared_ptr<const Packet>& packet,
832 const OnValidated& onValidated,
833 const OnFailed& onValidationFailed)
834{
Yingdi Yu80979ba2014-11-25 14:38:36 -0800835 if (signCertificate->getContentType() != tlv::ContentType_Key)
836 return onValidationFailed(packet,
837 "Cannot retrieve signer's cert: " +
838 signCertificate->getName().toUri());
839
840 shared_ptr<IdentityCertificate> certificate;
841 try {
842 certificate = make_shared<IdentityCertificate>(*signCertificate);
843 }
844 catch (tlv::Error&) {
845 return onValidationFailed(packet,
846 "Cannot decode signer's cert: " +
847 signCertificate->getName().toUri());
848 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800849
850 if (!certificate->isTooLate() && !certificate->isTooEarly())
851 {
852 if (static_cast<bool>(m_certificateCache))
853 m_certificateCache->insertCertificate(certificate);
854
855 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
856 return onValidated(packet);
857 else
858 return onValidationFailed(packet,
859 "Cannot verify signature: " +
860 packet->getName().toUri());
861 }
862 else
863 {
864 return onValidationFailed(packet,
865 "Signing certificate " +
866 signCertificate->getName().toUri() +
867 " is no longer valid.");
868 }
869}
870
871template<class Packet, class OnFailed>
872void
873ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
874 const std::string& failureInfo,
875 const shared_ptr<const Packet>& packet,
876 const OnFailed& onValidationFailed)
877{
878 onValidationFailed(packet, failureInfo);
879}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700880
881} // namespace ndn