blob: 13bcc73068cbe200bdde2ca65bc601e28a67475c [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{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080055 if (m_certificateCache == nullptr && face != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -080056 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{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080074 if (m_certificateCache == nullptr)
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());
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080083 if (!inputFile.good() || !inputFile.is_open()) {
84 std::string msg = "Failed to read configuration file: ";
85 msg += filename;
86 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
87 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070088 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;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800104 try {
105 boost::property_tree::read_info(input, tree);
106 }
107 catch (const boost::property_tree::info_parser_error& error) {
108 std::stringstream msg;
109 msg << "Failed to parse configuration file";
110 msg << " " << filename;
111 msg << " " << error.message() << " line " << error.line();
112 BOOST_THROW_EXCEPTION(security::conf::Error(msg.str()));
113 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700114
Yingdi Yudfa9d732014-04-09 09:53:01 -0700115 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700116}
117
118void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700119ValidatorConfig::load(const security::conf::ConfigSection& configSection,
120 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700121{
122 BOOST_ASSERT(!filename.empty());
123
Yingdi Yu58f33712014-04-16 16:57:47 -0700124 reset();
125
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800126 if (configSection.begin() == configSection.end()) {
127 std::string msg = "Error processing configuration file";
128 msg += ": ";
129 msg += filename;
130 msg += " no data";
131 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
132 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700133
134 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800135 i != configSection.end(); ++i) {
136 const std::string& sectionName = i->first;
137 const security::conf::ConfigSection& section = i->second;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700138
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800139 if (boost::iequals(sectionName, "rule")) {
140 onConfigRule(section, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700141 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800142 else if (boost::iequals(sectionName, "trust-anchor")) {
143 onConfigTrustAnchor(section, filename);
144 }
145 else {
146 std::string msg = "Error processing configuration file";
147 msg += " ";
148 msg += filename;
149 msg += " unrecognized section: " + sectionName;
150 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
151 }
152 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700153}
154
155void
156ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
157 const std::string& filename)
158{
159 using namespace ndn::security::conf;
160
161 ConfigSection::const_iterator propertyIt = configSection.begin();
162
163 // Get rule.id
164 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700165 BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700166
167 std::string ruleId = propertyIt->second.data();
168 propertyIt++;
169
170 // Get rule.for
171 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700172 BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700173
174 std::string usage = propertyIt->second.data();
175 propertyIt++;
176
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800177 bool isForData = false;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700178 if (boost::iequals(usage, "data"))
179 isForData = true;
180 else if (boost::iequals(usage, "interest"))
181 isForData = false;
182 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700183 BOOST_THROW_EXCEPTION(Error("Unrecognized <rule.for>: " + usage
184 + " in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700185
186 // Get rule.filter(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700187 std::vector<shared_ptr<Filter>> filters;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800188 for (; propertyIt != configSection.end(); propertyIt++) {
189 if (!boost::iequals(propertyIt->first, "filter")) {
190 if (boost::iequals(propertyIt->first, "checker"))
191 break;
192 BOOST_THROW_EXCEPTION(Error("Expect <rule.filter> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700193 }
194
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800195 filters.push_back(FilterFactory::create(propertyIt->second));
196 continue;
197 }
198
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700199 // Get rule.checker(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700200 std::vector<shared_ptr<Checker>> checkers;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800201 for (; propertyIt != configSection.end(); propertyIt++) {
202 if (!boost::iequals(propertyIt->first, "checker"))
203 BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700204
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800205 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
206 continue;
207 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700208
209 // Check other stuff
210 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700211 BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700212
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800213 if (checkers.empty())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700214 BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700215
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800216 if (isForData) {
217 shared_ptr<DataRule> rule = make_shared<DataRule>(ruleId);
218 for (const auto& filter : filters)
219 rule->addFilter(filter);
220 for (const auto& checker : checkers)
221 rule->addChecker(checker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700222
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800223 m_dataRules.push_back(rule);
224 }
225 else {
226 shared_ptr<InterestRule> rule = make_shared<InterestRule>(ruleId);;
227 for (const auto& filter : filters)
228 rule->addFilter(filter);
229 for (const auto& checker : checkers)
230 rule->addChecker(checker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700231
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800232 m_interestRules.push_back(rule);
233 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700234}
235
236void
237ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
238 const std::string& filename)
239{
240 using namespace ndn::security::conf;
241 using namespace boost::filesystem;
242
243 ConfigSection::const_iterator propertyIt = configSection.begin();
244
245 // Get trust-anchor.type
246 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700247 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.type>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700248
249 std::string type = propertyIt->second.data();
250 propertyIt++;
251
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800252 if (boost::iequals(type, "file")) {
253 // Get trust-anchor.file
254 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
255 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700256
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800257 std::string file = propertyIt->second.data();
258 propertyIt++;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700259
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800260 // Check other stuff
261 if (propertyIt != configSection.end())
262 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700263
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800264 path certfilePath = absolute(file, path(filename).parent_path());
265 auto idCert = io::load<v1::IdentityCertificate>(certfilePath.string());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700266
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800267 if (idCert != nullptr) {
268 BOOST_ASSERT(idCert->getName().size() >= 1);
269 m_staticContainer.add(idCert);
270 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
271 }
272 else
273 BOOST_THROW_EXCEPTION(Error("Cannot read certificate from file: " + certfilePath.native()));
274
275 return;
276 }
277 else if (boost::iequals(type, "base64")) {
278 // Get trust-anchor.base64-string
279 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
280 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!"));
281
282 std::stringstream ss(propertyIt->second.data());
283 propertyIt++;
284
285 // Check other stuff
286 if (propertyIt != configSection.end())
287 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
288
289 auto idCert = io::load<v1::IdentityCertificate>(ss);
290
291 if (idCert != nullptr) {
292 BOOST_ASSERT(idCert->getName().size() >= 1);
293 m_staticContainer.add(idCert);
294 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
295 }
296 else
297 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
298
299 return;
300 }
301 else if (boost::iequals(type, "dir")) {
302 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
303 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
304
305 std::string dirString(propertyIt->second.data());
306 propertyIt++;
307
308 if (propertyIt != configSection.end()) {
309 if (boost::iequals(propertyIt->first, "refresh")) {
310 using namespace boost::filesystem;
311
312 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
313 propertyIt++;
314
315 if (propertyIt != configSection.end())
316 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor"));
317
318 path dirPath = absolute(dirString, path(filename).parent_path());
319
320 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
321
322 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
323
324 return;
325 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700326 else
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800327 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.refresh>!"));
328 }
329 else {
330 using namespace boost::filesystem;
331
332 path dirPath = absolute(dirString, path(filename).parent_path());
333
334 directory_iterator end;
335
336 for (directory_iterator it(dirPath); it != end; it++) {
337 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
338
339 if (idCert != nullptr)
340 m_staticContainer.add(idCert);
341 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700342
343 return;
344 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800345 }
346 else if (boost::iequals(type, "any")) {
347 m_shouldValidate = false;
348 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700349 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700350 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700351}
352
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800353void
354ValidatorConfig::reset()
355{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800356 if (m_certificateCache != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800357 m_certificateCache->reset();
358 m_interestRules.clear();
359 m_dataRules.clear();
360
361 m_anchors.clear();
362
363 m_staticContainer = TrustAnchorContainer();
364
365 m_dynamicContainers.clear();
366}
367
368bool
369ValidatorConfig::isEmpty()
370{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800371 return ((m_certificateCache == nullptr || m_certificateCache->isEmpty()) &&
372 m_interestRules.empty() && m_dataRules.empty() && m_anchors.empty());
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800373}
374
Yingdi Yub4650652014-04-17 10:19:59 -0700375time::nanoseconds
376ValidatorConfig::getRefreshPeriod(std::string inputString)
377{
378 char unit = inputString[inputString.size() - 1];
379 std::string refreshString = inputString.substr(0, inputString.size() - 1);
380
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800381 uint32_t refreshPeriod = 0;
Yingdi Yub4650652014-04-17 10:19:59 -0700382
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800383 try {
384 refreshPeriod = boost::lexical_cast<uint32_t>(refreshString);
385 }
386 catch (const boost::bad_lexical_cast&) {
387 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
388 }
Yingdi Yub4650652014-04-17 10:19:59 -0700389
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800390 if (refreshPeriod == 0)
Yingdi Yub4650652014-04-17 10:19:59 -0700391 return getDefaultRefreshPeriod();
392
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800393 switch (unit) {
394 case 'h':
395 return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod));
396 case 'm':
397 return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod));
398 case 's':
399 return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod));
400 default:
401 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
402 }
Yingdi Yub4650652014-04-17 10:19:59 -0700403}
404
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800405time::nanoseconds
406ValidatorConfig::getDefaultRefreshPeriod()
407{
408 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
409}
410
Yingdi Yub4650652014-04-17 10:19:59 -0700411void
412ValidatorConfig::refreshAnchors()
413{
414 time::system_clock::TimePoint now = time::system_clock::now();
415
416 bool isRefreshed = false;
417
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800418 for (auto cIt = m_dynamicContainers.begin();
419 cIt != m_dynamicContainers.end() && cIt->getLastRefresh() + cIt->getRefreshPeriod() < now;
420 cIt++) {
421 isRefreshed = true;
422 cIt->refresh();
423 cIt->setLastRefresh(now);
424 }
425
426 if (isRefreshed) {
427 m_anchors.clear();
428
429 for (const auto& cert : m_staticContainer.getAll()) {
430 m_anchors[cert->getName().getPrefix(-1)] = cert;
Yingdi Yub4650652014-04-17 10:19:59 -0700431 }
432
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800433 for (const auto& container : m_dynamicContainers) {
434 const CertificateList& certList = container.getAll();
Yingdi Yub4650652014-04-17 10:19:59 -0700435
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800436 for (const auto& cert :certList) {
437 m_anchors[cert->getName().getPrefix(-1)] = cert;
438 }
Yingdi Yub4650652014-04-17 10:19:59 -0700439 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800440 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
441 }
Yingdi Yub4650652014-04-17 10:19:59 -0700442}
443
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700444void
445ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700446 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700447 const OnDataValidated& onValidated,
448 const OnDataValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700449 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700450{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700451 if (!m_shouldValidate)
452 return onValidated(data.shared_from_this());
453
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700454 bool isMatched = false;
455 int8_t checkResult = -1;
456
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800457 for (const auto& dataRule : m_dataRules) {
458 if (dataRule->match(data)) {
459 isMatched = true;
460 checkResult = dataRule->check(data, onValidated, onValidationFailed);
461 break;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700462 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800463 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700464
465 if (!isMatched)
466 return onValidationFailed(data.shared_from_this(), "No rule matched!");
467
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800468 if (checkResult == 0) {
469 const Signature& signature = data.getSignature();
470 checkSignature(data, signature, nSteps,
471 onValidated, onValidationFailed, nextSteps);
472 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700473}
474
475void
476ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700477 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700478 const OnInterestValidated& onValidated,
479 const OnInterestValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700480 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700481{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700482 if (!m_shouldValidate)
483 return onValidated(interest.shared_from_this());
484
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700485 // If interestName has less than 4 name components,
486 // it is definitely not a signed interest.
487 if (interest.getName().size() < signed_interest::MIN_LENGTH)
488 return onValidationFailed(interest.shared_from_this(),
489 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700490
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800491 try {
492 const Name& interestName = interest.getName();
493 Signature signature(interestName[signed_interest::POS_SIG_INFO].blockFromValue(),
494 interestName[signed_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700495
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800496 if (!signature.hasKeyLocator())
497 return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700498
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800499 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700500
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800501 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
502 return onValidationFailed(interest.shared_from_this(), "Key Locator is not a name");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700503
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800504 Name keyName = v1::IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700505
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800506 bool isMatched = false;
507 int8_t checkResult = -1;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700508
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800509 for (const auto& interestRule : m_interestRules) {
510 if (interestRule->match(interest)) {
511 isMatched = true;
512 checkResult = interestRule->check(interest,
513 bind(&ValidatorConfig::checkTimestamp, this, _1,
514 keyName, onValidated, onValidationFailed),
515 onValidationFailed);
516 break;
517 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700518 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800519
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800520 if (!isMatched)
521 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
522
523 if (checkResult == 0) {
524 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
525 (interest, signature, nSteps,
526 bind(&ValidatorConfig::checkTimestamp, this, _1,
527 keyName, onValidated, onValidationFailed),
528 onValidationFailed,
529 nextSteps);
Yingdi Yu80979ba2014-11-25 14:38:36 -0800530 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800531 }
532 catch (const Signature::Error& e) {
533 return onValidationFailed(interest.shared_from_this(), "No valid signature");
534 }
535 catch (const KeyLocator::Error& e){
536 return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator");
537 }
538 catch (const v1::IdentityCertificate::Error& e){
539 return onValidationFailed(interest.shared_from_this(), "Cannot determine the signing key");
540 }
541 catch (const tlv::Error& e){
542 return onValidationFailed(interest.shared_from_this(), "Cannot decode signature");
543 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700544}
545
546void
547ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
548 const Name& keyName,
549 const OnInterestValidated& onValidated,
550 const OnInterestValidationFailed& onValidationFailed)
551{
552 const Name& interestName = interest->getName();
553 time::system_clock::TimePoint interestTime;
554
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800555 try {
556 interestTime =
557 time::fromUnixTimestamp(time::milliseconds(interestName.get(-signed_interest::MIN_LENGTH).toNumber()));
558 }
559 catch (const tlv::Error& e) {
560 return onValidationFailed(interest,
561 "Cannot decode signature related TLVs");
562 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700563
564 time::system_clock::TimePoint currentTime = time::system_clock::now();
565
566 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800567 if (timestampIt == m_lastTimestamp.end()) {
568 if (!(currentTime - m_graceInterval <= interestTime &&
569 interestTime <= currentTime + m_graceInterval))
570 return onValidationFailed(interest,
571 "The command is not in grace interval: " + interest->getName().toUri());
572 }
573 else {
574 if (interestTime <= timestampIt->second)
575 return onValidationFailed(interest,
576 "The command is outdated: " + interest->getName().toUri());
577 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700578
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800579 // Update timestamp
580 if (timestampIt == m_lastTimestamp.end()) {
581 cleanOldKeys();
582 m_lastTimestamp[keyName] = interestTime;
583 }
584 else {
585 timestampIt->second = interestTime;
586 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700587
588 return onValidated(interest);
589}
590
591void
592ValidatorConfig::cleanOldKeys()
593{
594 if (m_lastTimestamp.size() < m_maxTrackedKeys)
595 return;
596
597 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
598 LastTimestampMap::iterator end = m_lastTimestamp.end();
599
600 time::system_clock::TimePoint now = time::system_clock::now();
601 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
602 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
603
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800604 while (timestampIt != end) {
605 if (now - timestampIt->second > m_keyTimestampTtl) {
606 LastTimestampMap::iterator toDelete = timestampIt;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700607 timestampIt++;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800608 m_lastTimestamp.erase(toDelete);
609 continue;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700610 }
611
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800612 if (timestampIt->second < oldestTimestamp) {
613 oldestTimestamp = timestampIt->second;
614 oldestKeyIt = timestampIt;
615 }
616
617 timestampIt++;
618 }
619
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700620 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
621 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700622}
623
Yingdi Yub4650652014-04-17 10:19:59 -0700624void
625ValidatorConfig::DynamicTrustAnchorContainer::refresh()
626{
627 using namespace boost::filesystem;
628
629 m_certificates.clear();
630
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800631 if (m_isDir) {
632 directory_iterator end;
Yingdi Yub4650652014-04-17 10:19:59 -0700633
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800634 for (directory_iterator it(m_path); it != end; it++) {
635 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
Yingdi Yub4650652014-04-17 10:19:59 -0700636
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800637 if (idCert != nullptr)
Yingdi Yub4650652014-04-17 10:19:59 -0700638 m_certificates.push_back(idCert);
639 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800640 }
641 else {
642 auto idCert = io::load<v1::IdentityCertificate>(m_path.string());
643
644 if (idCert != nullptr)
645 m_certificates.push_back(idCert);
646 }
Yingdi Yub4650652014-04-17 10:19:59 -0700647}
648
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800649template<class Packet, class OnValidated, class OnFailed>
650void
651ValidatorConfig::checkSignature(const Packet& packet,
652 const Signature& signature,
653 size_t nSteps,
654 const OnValidated& onValidated,
655 const OnFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700656 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800657{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800658 if (signature.getType() == tlv::DigestSha256) {
659 DigestSha256 sigSha256(signature);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800660
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800661 if (verifySignature(packet, sigSha256))
662 return onValidated(packet.shared_from_this());
663 else
664 return onValidationFailed(packet.shared_from_this(), "Sha256 Signature cannot be verified!");
665 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800666
667 try {
668 switch (signature.getType()) {
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800669 case tlv::SignatureSha256WithRsa:
670 case tlv::SignatureSha256WithEcdsa: {
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800671 if (!signature.hasKeyLocator()) {
672 return onValidationFailed(packet.shared_from_this(),
673 "Missing KeyLocator in SignatureInfo");
674 }
675 break;
676 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800677 default:
678 return onValidationFailed(packet.shared_from_this(), "Unsupported signature type");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800679 }
680 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800681 catch (const KeyLocator::Error& e) {
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800682 return onValidationFailed(packet.shared_from_this(),
683 "Cannot decode KeyLocator in public key signature");
684 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800685 catch (const tlv::Error& e) {
686 return onValidationFailed(packet.shared_from_this(), "Cannot decode public key signature");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800687 }
688
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800689 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
690 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
691 }
692
693 const Name& keyLocatorName = signature.getKeyLocator().getName();
694
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700695 shared_ptr<const v1::Certificate> trustedCert;
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800696
697 refreshAnchors();
698
699 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800700 if (m_anchors.end() == it && m_certificateCache != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800701 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700702 else if (m_anchors.end() != it)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800703 trustedCert = it->second;
704
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800705 if (trustedCert != nullptr) {
706 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
707 return onValidated(packet.shared_from_this());
708 else
709 return onValidationFailed(packet.shared_from_this(), "Cannot verify signature");
710 }
711 else {
712 if (m_stepLimit == nSteps)
713 return onValidationFailed(packet.shared_from_this(), "Maximum steps of validation reached");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800714
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800715 OnDataValidated onCertValidated =
716 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
717 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800718
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800719 OnDataValidationFailed onCertValidationFailed =
720 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
721 this, _1, _2, packet.shared_from_this(), onValidationFailed);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800722
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800723 Interest certInterest(keyLocatorName);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800724
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800725 auto nextStep = make_shared<ValidationRequest>(certInterest,
726 onCertValidated,
727 onCertValidationFailed,
728 1, nSteps + 1);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800729
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800730 nextSteps.push_back(nextStep);
731 return;
732 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800733 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
734}
735
736template<class Packet, class OnValidated, class OnFailed>
737void
738ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
739 const shared_ptr<const Packet>& packet,
740 const OnValidated& onValidated,
741 const OnFailed& onValidationFailed)
742{
Yingdi Yu80979ba2014-11-25 14:38:36 -0800743 if (signCertificate->getContentType() != tlv::ContentType_Key)
744 return onValidationFailed(packet,
745 "Cannot retrieve signer's cert: " +
746 signCertificate->getName().toUri());
747
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700748 shared_ptr<v1::IdentityCertificate> certificate;
Yingdi Yu80979ba2014-11-25 14:38:36 -0800749 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700750 certificate = make_shared<v1::IdentityCertificate>(*signCertificate);
Yingdi Yu80979ba2014-11-25 14:38:36 -0800751 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800752 catch (const tlv::Error&) {
Yingdi Yu80979ba2014-11-25 14:38:36 -0800753 return onValidationFailed(packet,
754 "Cannot decode signer's cert: " +
755 signCertificate->getName().toUri());
756 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800757
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800758 if (!certificate->isTooLate() && !certificate->isTooEarly()) {
759 if (m_certificateCache != nullptr)
760 m_certificateCache->insertCertificate(certificate);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800761
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800762 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
763 return onValidated(packet);
764 else
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800765 return onValidationFailed(packet,
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800766 "Cannot verify signature: " + packet->getName().toUri());
767 }
768 else {
769 return onValidationFailed(packet,
770 "Signing certificate " +
771 signCertificate->getName().toUri() + " is no longer valid.");
772 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800773}
774
775template<class Packet, class OnFailed>
776void
777ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
778 const std::string& failureInfo,
779 const shared_ptr<const Packet>& packet,
780 const OnFailed& onValidationFailed)
781{
782 onValidationFailed(packet, failureInfo);
783}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700784
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700785} // namespace security
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700786} // namespace ndn