blob: f643c84c497e7264c85e78bd872aad1aedf4fb05 [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 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070034namespace security {
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070035
36const shared_ptr<CertificateCache> ValidatorConfig::DEFAULT_CERTIFICATE_CACHE;
Yingdi Yu0f5fb692014-06-10 12:07:28 -070037const time::milliseconds ValidatorConfig::DEFAULT_GRACE_INTERVAL(3000);
38const time::system_clock::Duration ValidatorConfig::DEFAULT_KEY_TIMESTAMP_TTL = time::hours(1);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070039
Yingdi Yu4e9b0692014-11-04 16:13:56 -080040ValidatorConfig::ValidatorConfig(Face* face,
41 const shared_ptr<CertificateCache>& certificateCache,
42 const time::milliseconds& graceInterval,
43 const size_t stepLimit,
44 const size_t maxTrackedKeys,
45 const time::system_clock::Duration& keyTimestampTtl)
46 : Validator(face)
47 , m_shouldValidate(true)
48 , m_stepLimit(stepLimit)
49 , m_certificateCache(certificateCache)
50 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
51 DEFAULT_GRACE_INTERVAL : graceInterval)
52 , m_maxTrackedKeys(maxTrackedKeys)
53 , m_keyTimestampTtl(keyTimestampTtl)
54{
55 if (!static_cast<bool>(m_certificateCache) && face != nullptr)
56 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->getIoService()));
57}
58
Yingdi Yu96e64062014-04-15 19:57:33 -070059ValidatorConfig::ValidatorConfig(Face& face,
60 const shared_ptr<CertificateCache>& certificateCache,
Yingdi Yu0f5fb692014-06-10 12:07:28 -070061 const time::milliseconds& graceInterval,
62 const size_t stepLimit,
63 const size_t maxTrackedKeys,
64 const time::system_clock::Duration& keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070065 : Validator(face)
Yingdi Yu44d190c2014-04-16 17:05:46 -070066 , m_shouldValidate(true)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070067 , m_stepLimit(stepLimit)
68 , m_certificateCache(certificateCache)
Yingdi Yu0f5fb692014-06-10 12:07:28 -070069 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
70 DEFAULT_GRACE_INTERVAL : graceInterval)
71 , m_maxTrackedKeys(maxTrackedKeys)
72 , m_keyTimestampTtl(keyTimestampTtl)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070073{
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070074 if (!static_cast<bool>(m_certificateCache))
Yingdi Yu4e9b0692014-11-04 16:13:56 -080075 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.getIoService()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070076}
77
78void
79ValidatorConfig::load(const std::string& filename)
80{
81 std::ifstream inputFile;
82 inputFile.open(filename.c_str());
83 if (!inputFile.good() || !inputFile.is_open())
84 {
85 std::string msg = "Failed to read configuration file: ";
86 msg += filename;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070087 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070088 }
89 load(inputFile, filename);
90 inputFile.close();
91}
92
93void
94ValidatorConfig::load(const std::string& input, const std::string& filename)
95{
96 std::istringstream inputStream(input);
97 load(inputStream, filename);
98}
99
100
101void
102ValidatorConfig::load(std::istream& input, const std::string& filename)
103{
104 security::conf::ConfigSection tree;
105 try
106 {
107 boost::property_tree::read_info(input, tree);
108 }
Alexander Afanasyev2a7f7202014-04-23 14:25:29 -0700109 catch (boost::property_tree::info_parser_error& error)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700110 {
111 std::stringstream msg;
112 msg << "Failed to parse configuration file";
113 msg << " " << filename;
114 msg << " " << error.message() << " line " << error.line();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700115 BOOST_THROW_EXCEPTION(security::conf::Error(msg.str()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700116 }
117
Yingdi Yudfa9d732014-04-09 09:53:01 -0700118 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700119}
120
121void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700122ValidatorConfig::load(const security::conf::ConfigSection& configSection,
123 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700124{
125 BOOST_ASSERT(!filename.empty());
126
Yingdi Yu58f33712014-04-16 16:57:47 -0700127 reset();
128
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700129 if (configSection.begin() == configSection.end())
130 {
131 std::string msg = "Error processing configuration file";
132 msg += ": ";
133 msg += filename;
134 msg += " no data";
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700135 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700136 }
137
138 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
139 i != configSection.end(); ++i)
140 {
141 const std::string& sectionName = i->first;
142 const security::conf::ConfigSection& section = i->second;
143
144 if (boost::iequals(sectionName, "rule"))
145 {
146 onConfigRule(section, filename);
147 }
148 else if (boost::iequals(sectionName, "trust-anchor"))
149 {
150 onConfigTrustAnchor(section, filename);
151 }
152 else
153 {
154 std::string msg = "Error processing configuration file";
155 msg += " ";
156 msg += filename;
157 msg += " unrecognized section: " + sectionName;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700158 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700159 }
160 }
161}
162
163void
164ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
165 const std::string& filename)
166{
167 using namespace ndn::security::conf;
168
169 ConfigSection::const_iterator propertyIt = configSection.begin();
170
171 // Get rule.id
172 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700173 BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700174
175 std::string ruleId = propertyIt->second.data();
176 propertyIt++;
177
178 // Get rule.for
179 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700180 BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700181
182 std::string usage = propertyIt->second.data();
183 propertyIt++;
184
185 bool isForData;
186 if (boost::iequals(usage, "data"))
187 isForData = true;
188 else if (boost::iequals(usage, "interest"))
189 isForData = false;
190 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700191 BOOST_THROW_EXCEPTION(Error("Unrecognized <rule.for>: " + usage
192 + " in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700193
194 // Get rule.filter(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700195 std::vector<shared_ptr<Filter>> filters;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700196 for (; propertyIt != configSection.end(); propertyIt++)
197 {
198 if (!boost::iequals(propertyIt->first, "filter"))
199 {
200 if (boost::iequals(propertyIt->first, "checker"))
201 break;
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700202 BOOST_THROW_EXCEPTION(Error("Expect <rule.filter> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700203 }
204
205 filters.push_back(FilterFactory::create(propertyIt->second));
206 continue;
207 }
208
209 // Get rule.checker(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700210 std::vector<shared_ptr<Checker>> checkers;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700211 for (; propertyIt != configSection.end(); propertyIt++)
212 {
213 if (!boost::iequals(propertyIt->first, "checker"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700214 BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700215
216 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
217 continue;
218 }
219
220 // Check other stuff
221 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700222 BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700223
224 if (checkers.size() == 0)
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700225 BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700226
227 if (isForData)
228 {
229 shared_ptr<DataRule> rule(new DataRule(ruleId));
230 for (size_t i = 0; i < filters.size(); i++)
231 rule->addFilter(filters[i]);
232 for (size_t i = 0; i < checkers.size(); i++)
233 rule->addChecker(checkers[i]);
234
235 m_dataRules.push_back(rule);
236 }
237 else
238 {
239 shared_ptr<InterestRule> rule(new InterestRule(ruleId));
240 for (size_t i = 0; i < filters.size(); i++)
241 rule->addFilter(filters[i]);
242 for (size_t i = 0; i < checkers.size(); i++)
243 rule->addChecker(checkers[i]);
244
245 m_interestRules.push_back(rule);
246 }
247}
248
249void
250ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
251 const std::string& filename)
252{
253 using namespace ndn::security::conf;
254 using namespace boost::filesystem;
255
256 ConfigSection::const_iterator propertyIt = configSection.begin();
257
258 // Get trust-anchor.type
259 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700260 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.type>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700261
262 std::string type = propertyIt->second.data();
263 propertyIt++;
264
265 if (boost::iequals(type, "file"))
266 {
267 // Get trust-anchor.file
268 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"file-name"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700269 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700270
271 std::string file = propertyIt->second.data();
272 propertyIt++;
273
274 // Check other stuff
275 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700276 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700277
278 path certfilePath = absolute(file, path(filename).parent_path());
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700279 shared_ptr<v1::IdentityCertificate> idCert =
280 io::load<v1::IdentityCertificate>(certfilePath.string());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700281
282 if (static_cast<bool>(idCert))
283 {
284 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700285 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700286 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
287 }
288 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700289 BOOST_THROW_EXCEPTION(Error("Cannot read certificate from file: " +
290 certfilePath.native()));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700291
292 return;
293 }
294 else if (boost::iequals(type, "base64"))
295 {
296 // Get trust-anchor.base64-string
297 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700298 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700299
300 std::stringstream ss(propertyIt->second.data());
301 propertyIt++;
302
303 // Check other stuff
304 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700305 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700306
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700307 shared_ptr<v1::IdentityCertificate> idCert = io::load<v1::IdentityCertificate>(ss);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700308
309 if (static_cast<bool>(idCert))
310 {
311 BOOST_ASSERT(idCert->getName().size() >= 1);
Yingdi Yub4650652014-04-17 10:19:59 -0700312 m_staticContainer.add(idCert);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700313 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
314 }
315 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700316 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700317
318 return;
319 }
Yingdi Yub4650652014-04-17 10:19:59 -0700320 else if (boost::iequals(type, "dir"))
321 {
322 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700323 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
Yingdi Yub4650652014-04-17 10:19:59 -0700324
325 std::string dirString(propertyIt->second.data());
326 propertyIt++;
327
328 if (propertyIt != configSection.end())
329 {
330 if (boost::iequals(propertyIt->first, "refresh"))
331 {
332 using namespace boost::filesystem;
333
334 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
335 propertyIt++;
336
337 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700338 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor"));
Yingdi Yub4650652014-04-17 10:19:59 -0700339
340 path dirPath = absolute(dirString, path(filename).parent_path());
341
342 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
343
344 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
345
346 return;
347 }
348 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700349 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.refresh>!"));
Yingdi Yub4650652014-04-17 10:19:59 -0700350 }
351 else
352 {
353 using namespace boost::filesystem;
354
355 path dirPath = absolute(dirString, path(filename).parent_path());
356
357 directory_iterator end;
358
359 for (directory_iterator it(dirPath); it != end; it++)
360 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700361 shared_ptr<v1::IdentityCertificate> idCert =
362 io::load<v1::IdentityCertificate>(it->path().string());
Yingdi Yub4650652014-04-17 10:19:59 -0700363
364 if (static_cast<bool>(idCert))
365 m_staticContainer.add(idCert);
366 }
367
368 return;
369 }
370 }
Yingdi Yu44d190c2014-04-16 17:05:46 -0700371 else if (boost::iequals(type, "any"))
372 {
373 m_shouldValidate = false;
374 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700375 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700376 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700377}
378
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800379void
380ValidatorConfig::reset()
381{
382 if (static_cast<bool>(m_certificateCache))
383 m_certificateCache->reset();
384 m_interestRules.clear();
385 m_dataRules.clear();
386
387 m_anchors.clear();
388
389 m_staticContainer = TrustAnchorContainer();
390
391 m_dynamicContainers.clear();
392}
393
394bool
395ValidatorConfig::isEmpty()
396{
397 if ((!static_cast<bool>(m_certificateCache) || m_certificateCache->isEmpty()) &&
398 m_interestRules.empty() &&
399 m_dataRules.empty() &&
400 m_anchors.empty())
401 return true;
402 return false;
403}
404
Yingdi Yub4650652014-04-17 10:19:59 -0700405time::nanoseconds
406ValidatorConfig::getRefreshPeriod(std::string inputString)
407{
408 char unit = inputString[inputString.size() - 1];
409 std::string refreshString = inputString.substr(0, inputString.size() - 1);
410
411 uint32_t number;
412
413 try
414 {
415 number = boost::lexical_cast<uint32_t>(refreshString);
416 }
417 catch (boost::bad_lexical_cast&)
418 {
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700419 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
Yingdi Yub4650652014-04-17 10:19:59 -0700420 }
421
422 if (number == 0)
423 return getDefaultRefreshPeriod();
424
425 switch (unit)
426 {
427 case 'h':
428 return time::duration_cast<time::nanoseconds>(time::hours(number));
429 case 'm':
430 return time::duration_cast<time::nanoseconds>(time::minutes(number));
431 case 's':
432 return time::duration_cast<time::nanoseconds>(time::seconds(number));
433 default:
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700434 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
Yingdi Yub4650652014-04-17 10:19:59 -0700435 }
436}
437
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800438time::nanoseconds
439ValidatorConfig::getDefaultRefreshPeriod()
440{
441 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
442}
443
Yingdi Yub4650652014-04-17 10:19:59 -0700444void
445ValidatorConfig::refreshAnchors()
446{
447 time::system_clock::TimePoint now = time::system_clock::now();
448
449 bool isRefreshed = false;
450
451 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
452 cIt != m_dynamicContainers.end(); cIt++)
453 {
454 if (cIt->getLastRefresh() + cIt->getRefreshPeriod() < now)
455 {
456 isRefreshed = true;
457 cIt->refresh();
458 cIt->setLastRefresh(now);
459 }
460 else
461 break;
462 }
463
464 if (isRefreshed)
465 {
466 m_anchors.clear();
467
468 for (CertificateList::const_iterator it = m_staticContainer.getAll().begin();
469 it != m_staticContainer.getAll().end(); it++)
470 {
471 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
472 }
473
474 for (DynamicContainers::iterator cIt = m_dynamicContainers.begin();
475 cIt != m_dynamicContainers.end(); cIt++)
476 {
477 const CertificateList& certList = cIt->getAll();
478
479 for (CertificateList::const_iterator it = certList.begin();
480 it != certList.end(); it++)
481 {
482 m_anchors[(*it)->getName().getPrefix(-1)] = (*it);
483 }
484 }
485 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
486 }
487}
488
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700489void
490ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700491 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700492 const OnDataValidated& onValidated,
493 const OnDataValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700494 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700495{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700496 if (!m_shouldValidate)
497 return onValidated(data.shared_from_this());
498
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700499 bool isMatched = false;
500 int8_t checkResult = -1;
501
502 for (DataRuleList::iterator it = m_dataRules.begin();
503 it != m_dataRules.end(); it++)
504 {
505 if ((*it)->match(data))
506 {
507 isMatched = true;
508 checkResult = (*it)->check(data, onValidated, onValidationFailed);
509 break;
510 }
511 }
512
513 if (!isMatched)
514 return onValidationFailed(data.shared_from_this(), "No rule matched!");
515
516 if (checkResult == 0)
517 {
518 const Signature& signature = data.getSignature();
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700519 checkSignature(data, signature, nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700520 onValidated, onValidationFailed, nextSteps);
521 }
522}
523
524void
525ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700526 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700527 const OnInterestValidated& onValidated,
528 const OnInterestValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700529 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700530{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700531 if (!m_shouldValidate)
532 return onValidated(interest.shared_from_this());
533
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700534 // If interestName has less than 4 name components,
535 // it is definitely not a signed interest.
536 if (interest.getName().size() < signed_interest::MIN_LENGTH)
537 return onValidationFailed(interest.shared_from_this(),
538 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700539
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700540 try
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700541 {
542 const Name& interestName = interest.getName();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700543 Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(),
544 interestName[signed_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700545
Yingdi Yu4a557052014-07-09 16:40:37 -0700546 if (!signature.hasKeyLocator())
547 return onValidationFailed(interest.shared_from_this(),
548 "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700549
Yingdi Yu4a557052014-07-09 16:40:37 -0700550 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700551
552 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
553 return onValidationFailed(interest.shared_from_this(),
554 "Key Locator is not a name");
555
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700556 Name keyName = v1::IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700557
558 bool isMatched = false;
559 int8_t checkResult = -1;
560
561 for (InterestRuleList::iterator it = m_interestRules.begin();
562 it != m_interestRules.end(); it++)
563 {
564 if ((*it)->match(interest))
565 {
566 isMatched = true;
567 checkResult = (*it)->check(interest,
568 bind(&ValidatorConfig::checkTimestamp, this, _1,
569 keyName, onValidated, onValidationFailed),
570 onValidationFailed);
571 break;
572 }
573 }
574
575 if (!isMatched)
576 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
577
578 if (checkResult == 0)
579 {
580 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
Yingdi Yu4a557052014-07-09 16:40:37 -0700581 (interest, signature, nSteps,
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700582 bind(&ValidatorConfig::checkTimestamp, this, _1,
583 keyName, onValidated, onValidationFailed),
584 onValidationFailed,
585 nextSteps);
586 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700587 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700588 catch (Signature::Error& e)
589 {
590 return onValidationFailed(interest.shared_from_this(),
591 "No valid signature");
592 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700593 catch (KeyLocator::Error& e)
594 {
595 return onValidationFailed(interest.shared_from_this(),
596 "No valid KeyLocator");
597 }
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700598 catch (v1::IdentityCertificate::Error& e)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700599 {
600 return onValidationFailed(interest.shared_from_this(),
601 "Cannot determine the signing key");
602 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800603
604 catch (tlv::Error& e)
605 {
606 return onValidationFailed(interest.shared_from_this(),
607 "Cannot decode signature");
608 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700609}
610
611void
612ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
613 const Name& keyName,
614 const OnInterestValidated& onValidated,
615 const OnInterestValidationFailed& onValidationFailed)
616{
617 const Name& interestName = interest->getName();
618 time::system_clock::TimePoint interestTime;
619
620 try
621 {
622 interestTime =
623 time::fromUnixTimestamp(
624 time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber()));
625 }
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600626 catch (tlv::Error& e)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700627 {
628 return onValidationFailed(interest,
629 "Cannot decode signature related TLVs");
630 }
631
632 time::system_clock::TimePoint currentTime = time::system_clock::now();
633
634 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
635 if (timestampIt == m_lastTimestamp.end())
636 {
637 if (!(currentTime - m_graceInterval <= interestTime &&
638 interestTime <= currentTime + m_graceInterval))
639 return onValidationFailed(interest,
640 "The command is not in grace interval: " +
641 interest->getName().toUri());
642 }
643 else
644 {
645 if (interestTime <= timestampIt->second)
646 return onValidationFailed(interest,
647 "The command is outdated: " +
648 interest->getName().toUri());
649 }
650
651 //Update timestamp
652 if (timestampIt == m_lastTimestamp.end())
653 {
654 cleanOldKeys();
655 m_lastTimestamp[keyName] = interestTime;
656 }
657 else
658 {
659 timestampIt->second = interestTime;
660 }
661
662 return onValidated(interest);
663}
664
665void
666ValidatorConfig::cleanOldKeys()
667{
668 if (m_lastTimestamp.size() < m_maxTrackedKeys)
669 return;
670
671 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
672 LastTimestampMap::iterator end = m_lastTimestamp.end();
673
674 time::system_clock::TimePoint now = time::system_clock::now();
675 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
676 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
677
678 while (timestampIt != end)
679 {
680 if (now - timestampIt->second > m_keyTimestampTtl)
681 {
682 LastTimestampMap::iterator toDelete = timestampIt;
683 timestampIt++;
684 m_lastTimestamp.erase(toDelete);
685 continue;
686 }
687
688 if (timestampIt->second < oldestTimestamp)
689 {
690 oldestTimestamp = timestampIt->second;
691 oldestKeyIt = timestampIt;
692 }
693
694 timestampIt++;
695 }
696
697 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
698 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700699}
700
Yingdi Yub4650652014-04-17 10:19:59 -0700701void
702ValidatorConfig::DynamicTrustAnchorContainer::refresh()
703{
704 using namespace boost::filesystem;
705
706 m_certificates.clear();
707
708 if (m_isDir)
709 {
710 directory_iterator end;
711
712 for (directory_iterator it(m_path); it != end; it++)
713 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700714 shared_ptr<v1::IdentityCertificate> idCert =
715 io::load<v1::IdentityCertificate>(it->path().string());
Yingdi Yub4650652014-04-17 10:19:59 -0700716
717 if (static_cast<bool>(idCert))
718 m_certificates.push_back(idCert);
719 }
720 }
721 else
722 {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700723 shared_ptr<v1::IdentityCertificate> idCert =
724 io::load<v1::IdentityCertificate>(m_path.string());
Yingdi Yub4650652014-04-17 10:19:59 -0700725
726 if (static_cast<bool>(idCert))
727 m_certificates.push_back(idCert);
728 }
729}
730
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800731template<class Packet, class OnValidated, class OnFailed>
732void
733ValidatorConfig::checkSignature(const Packet& packet,
734 const Signature& signature,
735 size_t nSteps,
736 const OnValidated& onValidated,
737 const OnFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700738 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800739{
740 if (signature.getType() == tlv::DigestSha256)
741 {
742 DigestSha256 sigSha256(signature);
743
744 if (verifySignature(packet, sigSha256))
745 return onValidated(packet.shared_from_this());
746 else
747 return onValidationFailed(packet.shared_from_this(),
748 "Sha256 Signature cannot be verified!");
749 }
750
751 try {
752 switch (signature.getType()) {
753 case tlv::SignatureSha256WithRsa:
754 case tlv::SignatureSha256WithEcdsa:
755 {
756 if (!signature.hasKeyLocator()) {
757 return onValidationFailed(packet.shared_from_this(),
758 "Missing KeyLocator in SignatureInfo");
759 }
760 break;
761 }
762 default:
763 return onValidationFailed(packet.shared_from_this(),
764 "Unsupported signature type");
765 }
766 }
767 catch (KeyLocator::Error& e) {
768 return onValidationFailed(packet.shared_from_this(),
769 "Cannot decode KeyLocator in public key signature");
770 }
771 catch (tlv::Error& e) {
772 return onValidationFailed(packet.shared_from_this(),
773 "Cannot decode public key signature");
774 }
775
776
777 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
778 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
779 }
780
781 const Name& keyLocatorName = signature.getKeyLocator().getName();
782
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700783 shared_ptr<const v1::Certificate> trustedCert;
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800784
785 refreshAnchors();
786
787 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
788 if (m_anchors.end() == it && static_cast<bool>(m_certificateCache))
789 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700790 else if (m_anchors.end() != it)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800791 trustedCert = it->second;
792
793 if (static_cast<bool>(trustedCert))
794 {
795 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
796 return onValidated(packet.shared_from_this());
797 else
798 return onValidationFailed(packet.shared_from_this(),
799 "Cannot verify signature");
800 }
801 else
802 {
803 if (m_stepLimit == nSteps)
804 return onValidationFailed(packet.shared_from_this(),
805 "Maximum steps of validation reached");
806
807 OnDataValidated onCertValidated =
808 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
809 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
810
811 OnDataValidationFailed onCertValidationFailed =
812 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
813 this, _1, _2, packet.shared_from_this(), onValidationFailed);
814
815 Interest certInterest(keyLocatorName);
816
817 shared_ptr<ValidationRequest> nextStep =
818 make_shared<ValidationRequest>(certInterest,
819 onCertValidated,
820 onCertValidationFailed,
821 1, nSteps + 1);
822
823 nextSteps.push_back(nextStep);
824 return;
825 }
826
827 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
828}
829
830template<class Packet, class OnValidated, class OnFailed>
831void
832ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
833 const shared_ptr<const Packet>& packet,
834 const OnValidated& onValidated,
835 const OnFailed& onValidationFailed)
836{
Yingdi Yu80979ba2014-11-25 14:38:36 -0800837 if (signCertificate->getContentType() != tlv::ContentType_Key)
838 return onValidationFailed(packet,
839 "Cannot retrieve signer's cert: " +
840 signCertificate->getName().toUri());
841
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700842 shared_ptr<v1::IdentityCertificate> certificate;
Yingdi Yu80979ba2014-11-25 14:38:36 -0800843 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700844 certificate = make_shared<v1::IdentityCertificate>(*signCertificate);
Yingdi Yu80979ba2014-11-25 14:38:36 -0800845 }
846 catch (tlv::Error&) {
847 return onValidationFailed(packet,
848 "Cannot decode signer's cert: " +
849 signCertificate->getName().toUri());
850 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800851
852 if (!certificate->isTooLate() && !certificate->isTooEarly())
853 {
854 if (static_cast<bool>(m_certificateCache))
855 m_certificateCache->insertCertificate(certificate);
856
857 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
858 return onValidated(packet);
859 else
860 return onValidationFailed(packet,
861 "Cannot verify signature: " +
862 packet->getName().toUri());
863 }
864 else
865 {
866 return onValidationFailed(packet,
867 "Signing certificate " +
868 signCertificate->getName().toUri() +
869 " is no longer valid.");
870 }
871}
872
873template<class Packet, class OnFailed>
874void
875ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
876 const std::string& failureInfo,
877 const shared_ptr<const Packet>& packet,
878 const OnFailed& onValidationFailed)
879{
880 onValidationFailed(packet, failureInfo);
881}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700882
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700883} // namespace security
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700884} // namespace ndn