blob: f2673776057b0a21f3757ebbf13314ca90fdbb18 [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 Zhang48becde2017-01-05 16:41:38 -08003 * Copyright (c) 2013-2017 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.
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070020 */
21
22#include "validator-config.hpp"
23#include "certificate-cache-ttl.hpp"
24#include "../util/io.hpp"
Zhiyi Zhang48becde2017-01-05 16:41:38 -080025#include "../lp/tags.hpp"
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070026
27#include <boost/filesystem.hpp>
28#include <boost/property_tree/info_parser.hpp>
29#include <boost/algorithm/string.hpp>
30
31namespace ndn {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -070032namespace security {
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070033
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{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080053 if (m_certificateCache == nullptr && face != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -080054 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{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080072 if (m_certificateCache == nullptr)
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());
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -080081 if (!inputFile.good() || !inputFile.is_open()) {
82 std::string msg = "Failed to read configuration file: ";
83 msg += filename;
84 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
85 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -070086 load(inputFile, filename);
87 inputFile.close();
88}
89
90void
91ValidatorConfig::load(const std::string& input, const std::string& filename)
92{
93 std::istringstream inputStream(input);
94 load(inputStream, filename);
95}
96
97
98void
99ValidatorConfig::load(std::istream& input, const std::string& filename)
100{
101 security::conf::ConfigSection tree;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800102 try {
103 boost::property_tree::read_info(input, tree);
104 }
105 catch (const boost::property_tree::info_parser_error& error) {
106 std::stringstream msg;
107 msg << "Failed to parse configuration file";
108 msg << " " << filename;
109 msg << " " << error.message() << " line " << error.line();
110 BOOST_THROW_EXCEPTION(security::conf::Error(msg.str()));
111 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700112
Yingdi Yudfa9d732014-04-09 09:53:01 -0700113 load(tree, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700114}
115
116void
Yingdi Yudfa9d732014-04-09 09:53:01 -0700117ValidatorConfig::load(const security::conf::ConfigSection& configSection,
118 const std::string& filename)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700119{
120 BOOST_ASSERT(!filename.empty());
121
Yingdi Yu58f33712014-04-16 16:57:47 -0700122 reset();
123
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800124 if (configSection.begin() == configSection.end()) {
125 std::string msg = "Error processing configuration file";
126 msg += ": ";
127 msg += filename;
128 msg += " no data";
129 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
130 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700131
132 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800133 i != configSection.end(); ++i) {
134 const std::string& sectionName = i->first;
135 const security::conf::ConfigSection& section = i->second;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700136
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800137 if (boost::iequals(sectionName, "rule")) {
138 onConfigRule(section, filename);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700139 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800140 else if (boost::iequals(sectionName, "trust-anchor")) {
141 onConfigTrustAnchor(section, filename);
142 }
143 else {
144 std::string msg = "Error processing configuration file";
145 msg += " ";
146 msg += filename;
147 msg += " unrecognized section: " + sectionName;
148 BOOST_THROW_EXCEPTION(security::conf::Error(msg));
149 }
150 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700151}
152
153void
154ValidatorConfig::onConfigRule(const security::conf::ConfigSection& configSection,
155 const std::string& filename)
156{
157 using namespace ndn::security::conf;
158
159 ConfigSection::const_iterator propertyIt = configSection.begin();
160
161 // Get rule.id
162 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "id"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700163 BOOST_THROW_EXCEPTION(Error("Expect <rule.id>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700164
165 std::string ruleId = propertyIt->second.data();
166 propertyIt++;
167
168 // Get rule.for
169 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,"for"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700170 BOOST_THROW_EXCEPTION(Error("Expect <rule.for> in rule: " + ruleId + "!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700171
172 std::string usage = propertyIt->second.data();
173 propertyIt++;
174
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800175 bool isForData = false;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700176 if (boost::iequals(usage, "data"))
177 isForData = true;
178 else if (boost::iequals(usage, "interest"))
179 isForData = false;
180 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700181 BOOST_THROW_EXCEPTION(Error("Unrecognized <rule.for>: " + usage
182 + " in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700183
184 // Get rule.filter(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700185 std::vector<shared_ptr<Filter>> filters;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800186 for (; propertyIt != configSection.end(); propertyIt++) {
187 if (!boost::iequals(propertyIt->first, "filter")) {
188 if (boost::iequals(propertyIt->first, "checker"))
189 break;
190 BOOST_THROW_EXCEPTION(Error("Expect <rule.filter> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700191 }
192
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800193 filters.push_back(FilterFactory::create(propertyIt->second));
194 continue;
195 }
196
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700197 // Get rule.checker(s)
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700198 std::vector<shared_ptr<Checker>> checkers;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800199 for (; propertyIt != configSection.end(); propertyIt++) {
200 if (!boost::iequals(propertyIt->first, "checker"))
201 BOOST_THROW_EXCEPTION(Error("Expect <rule.checker> in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700202
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800203 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
204 continue;
205 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700206
207 // Check other stuff
208 if (propertyIt != configSection.end())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700209 BOOST_THROW_EXCEPTION(Error("Expect the end of rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700210
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800211 if (checkers.empty())
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700212 BOOST_THROW_EXCEPTION(Error("No <rule.checker> is specified in rule: " + ruleId));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700213
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800214 if (isForData) {
215 shared_ptr<DataRule> rule = make_shared<DataRule>(ruleId);
216 for (const auto& filter : filters)
217 rule->addFilter(filter);
218 for (const auto& checker : checkers)
219 rule->addChecker(checker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700220
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800221 m_dataRules.push_back(rule);
222 }
223 else {
224 shared_ptr<InterestRule> rule = make_shared<InterestRule>(ruleId);;
225 for (const auto& filter : filters)
226 rule->addFilter(filter);
227 for (const auto& checker : checkers)
228 rule->addChecker(checker);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700229
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800230 m_interestRules.push_back(rule);
231 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700232}
233
234void
235ValidatorConfig::onConfigTrustAnchor(const security::conf::ConfigSection& configSection,
236 const std::string& filename)
237{
238 using namespace ndn::security::conf;
239 using namespace boost::filesystem;
240
241 ConfigSection::const_iterator propertyIt = configSection.begin();
242
243 // Get trust-anchor.type
244 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "type"))
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700245 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.type>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700246
247 std::string type = propertyIt->second.data();
248 propertyIt++;
249
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800250 if (boost::iequals(type, "file")) {
251 // Get trust-anchor.file
252 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "file-name"))
253 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.file-name>!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700254
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800255 std::string file = propertyIt->second.data();
256 propertyIt++;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700257
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800258 // Check other stuff
259 if (propertyIt != configSection.end())
260 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700261
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800262 path certfilePath = absolute(file, path(filename).parent_path());
263 auto idCert = io::load<v1::IdentityCertificate>(certfilePath.string());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700264
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800265 if (idCert != nullptr) {
266 BOOST_ASSERT(idCert->getName().size() >= 1);
267 m_staticContainer.add(idCert);
268 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
269 }
270 else
271 BOOST_THROW_EXCEPTION(Error("Cannot read certificate from file: " + certfilePath.native()));
272
273 return;
274 }
275 else if (boost::iequals(type, "base64")) {
276 // Get trust-anchor.base64-string
277 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "base64-string"))
278 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.base64-string>!"));
279
280 std::stringstream ss(propertyIt->second.data());
281 propertyIt++;
282
283 // Check other stuff
284 if (propertyIt != configSection.end())
285 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor!"));
286
287 auto idCert = io::load<v1::IdentityCertificate>(ss);
288
289 if (idCert != nullptr) {
290 BOOST_ASSERT(idCert->getName().size() >= 1);
291 m_staticContainer.add(idCert);
292 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
293 }
294 else
295 BOOST_THROW_EXCEPTION(Error("Cannot decode certificate from base64-string"));
296
297 return;
298 }
299 else if (boost::iequals(type, "dir")) {
300 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first, "dir"))
301 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.dir>"));
302
303 std::string dirString(propertyIt->second.data());
304 propertyIt++;
305
306 if (propertyIt != configSection.end()) {
307 if (boost::iequals(propertyIt->first, "refresh")) {
308 using namespace boost::filesystem;
309
310 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
311 propertyIt++;
312
313 if (propertyIt != configSection.end())
314 BOOST_THROW_EXCEPTION(Error("Expect the end of trust-anchor"));
315
316 path dirPath = absolute(dirString, path(filename).parent_path());
317
318 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath, true, refresh));
319
320 m_dynamicContainers.rbegin()->setLastRefresh(time::system_clock::now() - refresh);
321
322 return;
323 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700324 else
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800325 BOOST_THROW_EXCEPTION(Error("Expect <trust-anchor.refresh>!"));
326 }
327 else {
328 using namespace boost::filesystem;
329
330 path dirPath = absolute(dirString, path(filename).parent_path());
331
332 directory_iterator end;
333
334 for (directory_iterator it(dirPath); it != end; it++) {
335 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
336
337 if (idCert != nullptr)
338 m_staticContainer.add(idCert);
339 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700340
341 return;
342 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800343 }
344 else if (boost::iequals(type, "any")) {
345 m_shouldValidate = false;
346 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700347 else
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700348 BOOST_THROW_EXCEPTION(Error("Unsupported trust-anchor.type: " + type));
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700349}
350
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800351void
352ValidatorConfig::reset()
353{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800354 if (m_certificateCache != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800355 m_certificateCache->reset();
356 m_interestRules.clear();
357 m_dataRules.clear();
358
359 m_anchors.clear();
360
361 m_staticContainer = TrustAnchorContainer();
362
363 m_dynamicContainers.clear();
364}
365
366bool
367ValidatorConfig::isEmpty()
368{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800369 return ((m_certificateCache == nullptr || m_certificateCache->isEmpty()) &&
370 m_interestRules.empty() && m_dataRules.empty() && m_anchors.empty());
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800371}
372
Yingdi Yub4650652014-04-17 10:19:59 -0700373time::nanoseconds
374ValidatorConfig::getRefreshPeriod(std::string inputString)
375{
376 char unit = inputString[inputString.size() - 1];
377 std::string refreshString = inputString.substr(0, inputString.size() - 1);
378
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800379 uint32_t refreshPeriod = 0;
Yingdi Yub4650652014-04-17 10:19:59 -0700380
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800381 try {
382 refreshPeriod = boost::lexical_cast<uint32_t>(refreshString);
383 }
384 catch (const boost::bad_lexical_cast&) {
385 BOOST_THROW_EXCEPTION(Error("Bad number: " + refreshString));
386 }
Yingdi Yub4650652014-04-17 10:19:59 -0700387
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800388 if (refreshPeriod == 0)
Yingdi Yub4650652014-04-17 10:19:59 -0700389 return getDefaultRefreshPeriod();
390
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800391 switch (unit) {
392 case 'h':
393 return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod));
394 case 'm':
395 return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod));
396 case 's':
397 return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod));
398 default:
399 BOOST_THROW_EXCEPTION(Error(std::string("Wrong time unit: ") + unit));
400 }
Yingdi Yub4650652014-04-17 10:19:59 -0700401}
402
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800403time::nanoseconds
404ValidatorConfig::getDefaultRefreshPeriod()
405{
406 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
407}
408
Yingdi Yub4650652014-04-17 10:19:59 -0700409void
410ValidatorConfig::refreshAnchors()
411{
412 time::system_clock::TimePoint now = time::system_clock::now();
413
414 bool isRefreshed = false;
415
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800416 for (auto cIt = m_dynamicContainers.begin();
417 cIt != m_dynamicContainers.end() && cIt->getLastRefresh() + cIt->getRefreshPeriod() < now;
418 cIt++) {
419 isRefreshed = true;
420 cIt->refresh();
421 cIt->setLastRefresh(now);
422 }
423
424 if (isRefreshed) {
425 m_anchors.clear();
426
427 for (const auto& cert : m_staticContainer.getAll()) {
428 m_anchors[cert->getName().getPrefix(-1)] = cert;
Yingdi Yub4650652014-04-17 10:19:59 -0700429 }
430
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800431 for (const auto& container : m_dynamicContainers) {
432 const CertificateList& certList = container.getAll();
Yingdi Yub4650652014-04-17 10:19:59 -0700433
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800434 for (const auto& cert :certList) {
435 m_anchors[cert->getName().getPrefix(-1)] = cert;
436 }
Yingdi Yub4650652014-04-17 10:19:59 -0700437 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800438 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
439 }
Yingdi Yub4650652014-04-17 10:19:59 -0700440}
441
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700442void
443ValidatorConfig::checkPolicy(const Data& data,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700444 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700445 const OnDataValidated& onValidated,
446 const OnDataValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700447 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700448{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700449 if (!m_shouldValidate)
450 return onValidated(data.shared_from_this());
451
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700452 bool isMatched = false;
453 int8_t checkResult = -1;
454
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800455 for (const auto& dataRule : m_dataRules) {
456 if (dataRule->match(data)) {
457 isMatched = true;
458 checkResult = dataRule->check(data, onValidated, onValidationFailed);
459 break;
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700460 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800461 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700462
463 if (!isMatched)
464 return onValidationFailed(data.shared_from_this(), "No rule matched!");
465
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800466 if (checkResult == 0) {
467 const Signature& signature = data.getSignature();
468 checkSignature(data, signature, nSteps,
469 onValidated, onValidationFailed, nextSteps);
470 }
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700471}
472
473void
474ValidatorConfig::checkPolicy(const Interest& interest,
Yingdi Yu4b8c6a22014-04-15 23:00:54 -0700475 int nSteps,
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700476 const OnInterestValidated& onValidated,
477 const OnInterestValidationFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700478 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700479{
Yingdi Yu44d190c2014-04-16 17:05:46 -0700480 if (!m_shouldValidate)
481 return onValidated(interest.shared_from_this());
482
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700483 // If interestName has less than 4 name components,
484 // it is definitely not a signed interest.
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800485 if (interest.getName().size() < command_interest::MIN_SIZE)
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700486 return onValidationFailed(interest.shared_from_this(),
487 "Interest is not signed: " + interest.getName().toUri());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700488
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800489 try {
490 const Name& interestName = interest.getName();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800491 Signature signature(interestName[command_interest::POS_SIG_INFO].blockFromValue(),
492 interestName[command_interest::POS_SIG_VALUE].blockFromValue());
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700493
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800494 if (!signature.hasKeyLocator())
495 return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700496
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800497 const KeyLocator& keyLocator = signature.getKeyLocator();
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700498
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800499 if (keyLocator.getType() != KeyLocator::KeyLocator_Name)
500 return onValidationFailed(interest.shared_from_this(), "Key Locator is not a name");
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700501
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800502 Name keyName = v1::IdentityCertificate::certificateNameToPublicKeyName(keyLocator.getName());
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700503
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800504 bool isMatched = false;
505 int8_t checkResult = -1;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700506
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800507 for (const auto& interestRule : m_interestRules) {
508 if (interestRule->match(interest)) {
509 isMatched = true;
510 checkResult = interestRule->check(interest,
511 bind(&ValidatorConfig::checkTimestamp, this, _1,
512 keyName, onValidated, onValidationFailed),
513 onValidationFailed);
514 break;
515 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700516 }
Yingdi Yu80979ba2014-11-25 14:38:36 -0800517
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800518 if (!isMatched)
519 return onValidationFailed(interest.shared_from_this(), "No rule matched!");
520
521 if (checkResult == 0) {
522 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
523 (interest, signature, nSteps,
524 bind(&ValidatorConfig::checkTimestamp, this, _1,
525 keyName, onValidated, onValidationFailed),
526 onValidationFailed,
527 nextSteps);
Yingdi Yu80979ba2014-11-25 14:38:36 -0800528 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800529 }
530 catch (const Signature::Error& e) {
531 return onValidationFailed(interest.shared_from_this(), "No valid signature");
532 }
533 catch (const KeyLocator::Error& e){
534 return onValidationFailed(interest.shared_from_this(), "No valid KeyLocator");
535 }
536 catch (const v1::IdentityCertificate::Error& e){
537 return onValidationFailed(interest.shared_from_this(), "Cannot determine the signing key");
538 }
539 catch (const tlv::Error& e){
540 return onValidationFailed(interest.shared_from_this(), "Cannot decode signature");
541 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700542}
543
544void
545ValidatorConfig::checkTimestamp(const shared_ptr<const Interest>& interest,
546 const Name& keyName,
547 const OnInterestValidated& onValidated,
548 const OnInterestValidationFailed& onValidationFailed)
549{
550 const Name& interestName = interest->getName();
551 time::system_clock::TimePoint interestTime;
552
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800553 try {
554 interestTime =
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800555 time::fromUnixTimestamp(time::milliseconds(interestName.at(command_interest::POS_TIMESTAMP).toNumber()));
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800556 }
557 catch (const tlv::Error& e) {
558 return onValidationFailed(interest,
559 "Cannot decode signature related TLVs");
560 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700561
562 time::system_clock::TimePoint currentTime = time::system_clock::now();
563
564 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800565 if (timestampIt == m_lastTimestamp.end()) {
566 if (!(currentTime - m_graceInterval <= interestTime &&
567 interestTime <= currentTime + m_graceInterval))
568 return onValidationFailed(interest,
569 "The command is not in grace interval: " + interest->getName().toUri());
570 }
571 else {
572 if (interestTime <= timestampIt->second)
573 return onValidationFailed(interest,
574 "The command is outdated: " + interest->getName().toUri());
575 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700576
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800577 // Update timestamp
578 if (timestampIt == m_lastTimestamp.end()) {
579 cleanOldKeys();
580 m_lastTimestamp[keyName] = interestTime;
581 }
582 else {
583 timestampIt->second = interestTime;
584 }
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700585
586 return onValidated(interest);
587}
588
589void
590ValidatorConfig::cleanOldKeys()
591{
592 if (m_lastTimestamp.size() < m_maxTrackedKeys)
593 return;
594
595 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
596 LastTimestampMap::iterator end = m_lastTimestamp.end();
597
598 time::system_clock::TimePoint now = time::system_clock::now();
599 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
600 time::system_clock::TimePoint oldestTimestamp = oldestKeyIt->second;
601
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800602 while (timestampIt != end) {
603 if (now - timestampIt->second > m_keyTimestampTtl) {
604 LastTimestampMap::iterator toDelete = timestampIt;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700605 timestampIt++;
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800606 m_lastTimestamp.erase(toDelete);
607 continue;
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700608 }
609
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800610 if (timestampIt->second < oldestTimestamp) {
611 oldestTimestamp = timestampIt->second;
612 oldestKeyIt = timestampIt;
613 }
614
615 timestampIt++;
616 }
617
Yingdi Yu0f5fb692014-06-10 12:07:28 -0700618 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
619 m_lastTimestamp.erase(oldestKeyIt);
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700620}
621
Yingdi Yub4650652014-04-17 10:19:59 -0700622void
623ValidatorConfig::DynamicTrustAnchorContainer::refresh()
624{
625 using namespace boost::filesystem;
626
627 m_certificates.clear();
628
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800629 if (m_isDir) {
630 directory_iterator end;
Yingdi Yub4650652014-04-17 10:19:59 -0700631
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800632 for (directory_iterator it(m_path); it != end; it++) {
633 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
Yingdi Yub4650652014-04-17 10:19:59 -0700634
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800635 if (idCert != nullptr)
Yingdi Yub4650652014-04-17 10:19:59 -0700636 m_certificates.push_back(idCert);
637 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800638 }
639 else {
640 auto idCert = io::load<v1::IdentityCertificate>(m_path.string());
641
642 if (idCert != nullptr)
643 m_certificates.push_back(idCert);
644 }
Yingdi Yub4650652014-04-17 10:19:59 -0700645}
646
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800647template<class Packet, class OnValidated, class OnFailed>
648void
649ValidatorConfig::checkSignature(const Packet& packet,
650 const Signature& signature,
651 size_t nSteps,
652 const OnValidated& onValidated,
653 const OnFailed& onValidationFailed,
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700654 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800655{
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800656 if (signature.getType() == tlv::DigestSha256) {
657 DigestSha256 sigSha256(signature);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800658
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800659 if (verifySignature(packet, sigSha256))
660 return onValidated(packet.shared_from_this());
661 else
662 return onValidationFailed(packet.shared_from_this(), "Sha256 Signature cannot be verified!");
663 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800664
665 try {
666 switch (signature.getType()) {
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800667 case tlv::SignatureSha256WithRsa:
668 case tlv::SignatureSha256WithEcdsa: {
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800669 if (!signature.hasKeyLocator()) {
670 return onValidationFailed(packet.shared_from_this(),
671 "Missing KeyLocator in SignatureInfo");
672 }
673 break;
674 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800675 default:
676 return onValidationFailed(packet.shared_from_this(), "Unsupported signature type");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800677 }
678 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800679 catch (const KeyLocator::Error& e) {
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800680 return onValidationFailed(packet.shared_from_this(),
681 "Cannot decode KeyLocator in public key signature");
682 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800683 catch (const tlv::Error& e) {
684 return onValidationFailed(packet.shared_from_this(), "Cannot decode public key signature");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800685 }
686
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800687 if (signature.getKeyLocator().getType() != KeyLocator::KeyLocator_Name) {
688 return onValidationFailed(packet.shared_from_this(), "Unsupported KeyLocator type");
689 }
690
691 const Name& keyLocatorName = signature.getKeyLocator().getName();
692
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700693 shared_ptr<const v1::Certificate> trustedCert;
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800694
695 refreshAnchors();
696
697 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800698 if (m_anchors.end() == it && m_certificateCache != nullptr)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800699 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
Zhiyi Zhang044bb7e2016-06-10 00:02:37 -0700700 else if (m_anchors.end() != it)
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800701 trustedCert = it->second;
702
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800703 if (trustedCert != nullptr) {
704 if (verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
705 return onValidated(packet.shared_from_this());
706 else
707 return onValidationFailed(packet.shared_from_this(), "Cannot verify signature");
708 }
709 else {
710 if (m_stepLimit == nSteps)
711 return onValidationFailed(packet.shared_from_this(), "Maximum steps of validation reached");
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800712
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800713 OnDataValidated onCertValidated =
714 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
715 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800716
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800717 OnDataValidationFailed onCertValidationFailed =
718 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
719 this, _1, _2, packet.shared_from_this(), onValidationFailed);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800720
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800721 Interest certInterest(keyLocatorName);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800722
Zhiyi Zhang48becde2017-01-05 16:41:38 -0800723 uint64_t incomingFaceId = 0;
724 auto incomingFaceIdTag = packet.template getTag<lp::IncomingFaceIdTag>();
725 if (incomingFaceIdTag != nullptr) {
726 incomingFaceId = incomingFaceIdTag->get();
727 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800728 auto nextStep = make_shared<ValidationRequest>(certInterest,
729 onCertValidated,
730 onCertValidationFailed,
Zhiyi Zhang48becde2017-01-05 16:41:38 -0800731 1, nSteps + 1,
732 incomingFaceId);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800733
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800734 nextSteps.push_back(nextStep);
735 return;
736 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800737 return onValidationFailed(packet.shared_from_this(), "Unsupported Signature Type");
738}
739
740template<class Packet, class OnValidated, class OnFailed>
741void
742ValidatorConfig::onCertValidated(const shared_ptr<const Data>& signCertificate,
743 const shared_ptr<const Packet>& packet,
744 const OnValidated& onValidated,
745 const OnFailed& onValidationFailed)
746{
Yingdi Yu80979ba2014-11-25 14:38:36 -0800747 if (signCertificate->getContentType() != tlv::ContentType_Key)
748 return onValidationFailed(packet,
749 "Cannot retrieve signer's cert: " +
750 signCertificate->getName().toUri());
751
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700752 shared_ptr<v1::IdentityCertificate> certificate;
Yingdi Yu80979ba2014-11-25 14:38:36 -0800753 try {
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700754 certificate = make_shared<v1::IdentityCertificate>(*signCertificate);
Yingdi Yu80979ba2014-11-25 14:38:36 -0800755 }
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800756 catch (const tlv::Error&) {
Yingdi Yu80979ba2014-11-25 14:38:36 -0800757 return onValidationFailed(packet,
758 "Cannot decode signer's cert: " +
759 signCertificate->getName().toUri());
760 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800761
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800762 if (!certificate->isTooLate() && !certificate->isTooEarly()) {
763 if (m_certificateCache != nullptr)
764 m_certificateCache->insertCertificate(certificate);
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800765
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800766 if (verifySignature(*packet, certificate->getPublicKeyInfo()))
767 return onValidated(packet);
768 else
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800769 return onValidationFailed(packet,
Zhiyi Zhang7ba99e12016-11-10 14:26:16 -0800770 "Cannot verify signature: " + packet->getName().toUri());
771 }
772 else {
773 return onValidationFailed(packet,
774 "Signing certificate " +
775 signCertificate->getName().toUri() + " is no longer valid.");
776 }
Yingdi Yu4e9b0692014-11-04 16:13:56 -0800777}
778
779template<class Packet, class OnFailed>
780void
781ValidatorConfig::onCertFailed(const shared_ptr<const Data>& signCertificate,
782 const std::string& failureInfo,
783 const shared_ptr<const Packet>& packet,
784 const OnFailed& onValidationFailed)
785{
786 onValidationFailed(packet, failureInfo);
787}
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700788
Alexander Afanasyev2fa59392016-07-29 17:24:23 -0700789} // namespace security
Yingdi Yu48e8c0c2014-03-19 12:01:55 -0700790} // namespace ndn