24 #include "../util/io.hpp"
25 #include "../lp/tags.hpp"
27 #include <boost/filesystem.hpp>
28 #include <boost/property_tree/info_parser.hpp>
29 #include <boost/algorithm/string.hpp>
39 const shared_ptr<CertificateCache>& certificateCache,
40 const time::milliseconds& graceInterval,
41 const size_t stepLimit,
42 const size_t maxTrackedKeys,
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)
53 if (m_certificateCache ==
nullptr && face !=
nullptr)
54 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face->
getIoService()));
58 const shared_ptr<CertificateCache>& certificateCache,
59 const time::milliseconds& graceInterval,
60 const size_t stepLimit,
61 const size_t maxTrackedKeys,
64 , m_shouldValidate(true)
65 , m_stepLimit(stepLimit)
66 , m_certificateCache(certificateCache)
67 , m_graceInterval(graceInterval < time::milliseconds::zero() ?
68 DEFAULT_GRACE_INTERVAL : graceInterval)
69 , m_maxTrackedKeys(maxTrackedKeys)
70 , m_keyTimestampTtl(keyTimestampTtl)
72 if (m_certificateCache ==
nullptr)
73 m_certificateCache = make_shared<CertificateCacheTtl>(ref(face.
getIoService()));
79 std::ifstream inputFile;
80 inputFile.open(filename.c_str());
81 if (!inputFile.good() || !inputFile.is_open()) {
82 std::string msg =
"Failed to read configuration file: ";
86 load(inputFile, filename);
93 std::istringstream inputStream(input);
94 load(inputStream, filename);
103 boost::property_tree::read_info(input, tree);
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();
113 load(tree, filename);
118 const std::string& filename)
120 BOOST_ASSERT(!filename.empty());
124 if (configSection.begin() == configSection.end()) {
125 std::string msg =
"Error processing configuration file";
132 for (security::conf::ConfigSection::const_iterator i = configSection.begin();
133 i != configSection.end(); ++i) {
134 const std::string& sectionName = i->first;
137 if (boost::iequals(sectionName,
"rule")) {
138 onConfigRule(section, filename);
140 else if (boost::iequals(sectionName,
"trust-anchor")) {
141 onConfigTrustAnchor(section, filename);
144 std::string msg =
"Error processing configuration file";
147 msg +=
" unrecognized section: " + sectionName;
155 const std::string& filename)
159 ConfigSection::const_iterator propertyIt = configSection.begin();
162 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,
"id"))
163 BOOST_THROW_EXCEPTION(
Error(
"Expect <rule.id>!"));
165 std::string ruleId = propertyIt->second.data();
169 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,
"for"))
170 BOOST_THROW_EXCEPTION(
Error(
"Expect <rule.for> in rule: " + ruleId +
"!"));
172 std::string usage = propertyIt->second.data();
175 bool isForData =
false;
176 if (boost::iequals(usage,
"data"))
178 else if (boost::iequals(usage,
"interest"))
181 BOOST_THROW_EXCEPTION(
Error(
"Unrecognized <rule.for>: " + usage
182 +
" in rule: " + ruleId));
185 std::vector<shared_ptr<Filter>> filters;
186 for (; propertyIt != configSection.end(); propertyIt++) {
187 if (!boost::iequals(propertyIt->first,
"filter")) {
188 if (boost::iequals(propertyIt->first,
"checker"))
190 BOOST_THROW_EXCEPTION(
Error(
"Expect <rule.filter> in rule: " + ruleId));
193 filters.push_back(FilterFactory::create(propertyIt->second));
198 std::vector<shared_ptr<Checker>> checkers;
199 for (; propertyIt != configSection.end(); propertyIt++) {
200 if (!boost::iequals(propertyIt->first,
"checker"))
201 BOOST_THROW_EXCEPTION(
Error(
"Expect <rule.checker> in rule: " + ruleId));
203 checkers.push_back(CheckerFactory::create(propertyIt->second, filename));
208 if (propertyIt != configSection.end())
209 BOOST_THROW_EXCEPTION(
Error(
"Expect the end of rule: " + ruleId));
211 if (checkers.empty())
212 BOOST_THROW_EXCEPTION(
Error(
"No <rule.checker> is specified in rule: " + ruleId));
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);
221 m_dataRules.push_back(rule);
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);
230 m_interestRules.push_back(rule);
236 const std::string& filename)
239 using namespace boost::filesystem;
241 ConfigSection::const_iterator propertyIt = configSection.begin();
244 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,
"type"))
245 BOOST_THROW_EXCEPTION(
Error(
"Expect <trust-anchor.type>!"));
247 std::string type = propertyIt->second.data();
250 if (boost::iequals(type,
"file")) {
252 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,
"file-name"))
253 BOOST_THROW_EXCEPTION(
Error(
"Expect <trust-anchor.file-name>!"));
255 std::string file = propertyIt->second.data();
259 if (propertyIt != configSection.end())
260 BOOST_THROW_EXCEPTION(
Error(
"Expect the end of trust-anchor!"));
262 path certfilePath = absolute(file, path(filename).parent_path());
263 auto idCert = io::load<v1::IdentityCertificate>(certfilePath.string());
265 if (idCert !=
nullptr) {
266 BOOST_ASSERT(idCert->getName().size() >= 1);
267 m_staticContainer.add(idCert);
268 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
271 BOOST_THROW_EXCEPTION(
Error(
"Cannot read certificate from file: " + certfilePath.native()));
275 else if (boost::iequals(type,
"base64")) {
277 if (propertyIt == configSection.end() || !boost::iequals(propertyIt->first,
"base64-string"))
278 BOOST_THROW_EXCEPTION(
Error(
"Expect <trust-anchor.base64-string>!"));
280 std::stringstream ss(propertyIt->second.data());
284 if (propertyIt != configSection.end())
285 BOOST_THROW_EXCEPTION(
Error(
"Expect the end of trust-anchor!"));
287 auto idCert = io::load<v1::IdentityCertificate>(ss);
289 if (idCert !=
nullptr) {
290 BOOST_ASSERT(idCert->getName().size() >= 1);
291 m_staticContainer.add(idCert);
292 m_anchors[idCert->getName().getPrefix(-1)] = idCert;
295 BOOST_THROW_EXCEPTION(
Error(
"Cannot decode certificate from base64-string"));
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>"));
303 std::string dirString(propertyIt->second.data());
306 if (propertyIt != configSection.end()) {
307 if (boost::iequals(propertyIt->first,
"refresh")) {
308 using namespace boost::filesystem;
310 time::nanoseconds refresh = getRefreshPeriod(propertyIt->second.data());
313 if (propertyIt != configSection.end())
314 BOOST_THROW_EXCEPTION(
Error(
"Expect the end of trust-anchor"));
316 path dirPath = absolute(dirString, path(filename).parent_path());
318 m_dynamicContainers.push_back(DynamicTrustAnchorContainer(dirPath,
true, refresh));
325 BOOST_THROW_EXCEPTION(
Error(
"Expect <trust-anchor.refresh>!"));
328 using namespace boost::filesystem;
330 path dirPath = absolute(dirString, path(filename).parent_path());
332 directory_iterator end;
334 for (directory_iterator it(dirPath); it != end; it++) {
335 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
337 if (idCert !=
nullptr)
338 m_staticContainer.add(idCert);
344 else if (boost::iequals(type,
"any")) {
345 m_shouldValidate =
false;
348 BOOST_THROW_EXCEPTION(
Error(
"Unsupported trust-anchor.type: " + type));
354 if (m_certificateCache !=
nullptr)
355 m_certificateCache->reset();
356 m_interestRules.clear();
361 m_staticContainer = TrustAnchorContainer();
363 m_dynamicContainers.clear();
369 return ((m_certificateCache ==
nullptr || m_certificateCache->isEmpty()) &&
370 m_interestRules.empty() && m_dataRules.empty() && m_anchors.empty());
374 ValidatorConfig::getRefreshPeriod(std::string inputString)
376 char unit = inputString[inputString.size() - 1];
377 std::string refreshString = inputString.substr(0, inputString.size() - 1);
379 uint32_t refreshPeriod = 0;
382 refreshPeriod = boost::lexical_cast<uint32_t>(refreshString);
384 catch (
const boost::bad_lexical_cast&) {
385 BOOST_THROW_EXCEPTION(
Error(
"Bad number: " + refreshString));
388 if (refreshPeriod == 0)
389 return getDefaultRefreshPeriod();
393 return time::duration_cast<time::nanoseconds>(time::hours(refreshPeriod));
395 return time::duration_cast<time::nanoseconds>(time::minutes(refreshPeriod));
397 return time::duration_cast<time::nanoseconds>(time::seconds(refreshPeriod));
399 BOOST_THROW_EXCEPTION(
Error(std::string(
"Wrong time unit: ") + unit));
404 ValidatorConfig::getDefaultRefreshPeriod()
406 return time::duration_cast<time::nanoseconds>(time::seconds(3600));
410 ValidatorConfig::refreshAnchors()
414 bool isRefreshed =
false;
416 for (
auto cIt = m_dynamicContainers.begin();
417 cIt != m_dynamicContainers.end() && cIt->getLastRefresh() + cIt->getRefreshPeriod() < now;
421 cIt->setLastRefresh(now);
427 for (
const auto& cert : m_staticContainer.getAll()) {
428 m_anchors[cert->getName().getPrefix(-1)] = cert;
431 for (
const auto& container : m_dynamicContainers) {
432 const CertificateList& certList = container.getAll();
434 for (
const auto& cert :certList) {
435 m_anchors[cert->getName().getPrefix(-1)] = cert;
438 m_dynamicContainers.sort(ValidatorConfig::compareDynamicContainer);
447 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
449 if (!m_shouldValidate)
450 return onValidated(data.shared_from_this());
452 bool isMatched =
false;
453 int8_t checkResult = -1;
455 for (
const auto& dataRule : m_dataRules) {
456 if (dataRule->match(data)) {
458 checkResult = dataRule->check(data, onValidated, onValidationFailed);
464 return onValidationFailed(data.shared_from_this(),
"No rule matched!");
466 if (checkResult == 0) {
468 checkSignature(data, signature, nSteps,
469 onValidated, onValidationFailed, nextSteps);
478 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
480 if (!m_shouldValidate)
481 return onValidated(interest.shared_from_this());
486 return onValidationFailed(interest.shared_from_this(),
487 "Interest is not signed: " + interest.
getName().
toUri());
495 return onValidationFailed(interest.shared_from_this(),
"No valid KeyLocator");
500 return onValidationFailed(interest.shared_from_this(),
"Key Locator is not a name");
504 bool isMatched =
false;
505 int8_t checkResult = -1;
507 for (
const auto& interestRule : m_interestRules) {
508 if (interestRule->match(interest)) {
510 checkResult = interestRule->check(interest,
511 bind(&ValidatorConfig::checkTimestamp,
this, _1,
512 keyName, onValidated, onValidationFailed),
519 return onValidationFailed(interest.shared_from_this(),
"No rule matched!");
521 if (checkResult == 0) {
522 checkSignature<Interest, OnInterestValidated, OnInterestValidationFailed>
523 (interest, signature, nSteps,
524 bind(&ValidatorConfig::checkTimestamp,
this, _1,
525 keyName, onValidated, onValidationFailed),
531 return onValidationFailed(interest.shared_from_this(),
"No valid signature");
534 return onValidationFailed(interest.shared_from_this(),
"No valid KeyLocator");
537 return onValidationFailed(interest.shared_from_this(),
"Cannot determine the signing key");
540 return onValidationFailed(interest.shared_from_this(),
"Cannot decode signature");
545 ValidatorConfig::checkTimestamp(
const shared_ptr<const Interest>& interest,
550 const Name& interestName = interest->getName();
558 return onValidationFailed(interest,
559 "Cannot decode signature related TLVs");
564 LastTimestampMap::iterator timestampIt = m_lastTimestamp.find(keyName);
565 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());
572 if (interestTime <= timestampIt->second)
573 return onValidationFailed(interest,
574 "The command is outdated: " + interest->getName().toUri());
578 if (timestampIt == m_lastTimestamp.end()) {
580 m_lastTimestamp[keyName] = interestTime;
583 timestampIt->second = interestTime;
586 return onValidated(interest);
590 ValidatorConfig::cleanOldKeys()
592 if (m_lastTimestamp.size() < m_maxTrackedKeys)
595 LastTimestampMap::iterator timestampIt = m_lastTimestamp.begin();
596 LastTimestampMap::iterator end = m_lastTimestamp.end();
599 LastTimestampMap::iterator oldestKeyIt = m_lastTimestamp.begin();
602 while (timestampIt != end) {
603 if (now - timestampIt->second > m_keyTimestampTtl) {
604 LastTimestampMap::iterator toDelete = timestampIt;
606 m_lastTimestamp.erase(toDelete);
610 if (timestampIt->second < oldestTimestamp) {
611 oldestTimestamp = timestampIt->second;
612 oldestKeyIt = timestampIt;
618 if (m_lastTimestamp.size() >= m_maxTrackedKeys)
619 m_lastTimestamp.erase(oldestKeyIt);
623 ValidatorConfig::DynamicTrustAnchorContainer::refresh()
625 using namespace boost::filesystem;
627 m_certificates.clear();
630 directory_iterator end;
632 for (directory_iterator it(m_path); it != end; it++) {
633 auto idCert = io::load<v1::IdentityCertificate>(it->path().string());
635 if (idCert !=
nullptr)
636 m_certificates.push_back(idCert);
640 auto idCert = io::load<v1::IdentityCertificate>(m_path.string());
642 if (idCert !=
nullptr)
643 m_certificates.push_back(idCert);
647 template<
class Packet,
class OnVal
idated,
class OnFailed>
649 ValidatorConfig::checkSignature(
const Packet& packet,
650 const Signature& signature,
652 const OnValidated& onValidated,
653 const OnFailed& onValidationFailed,
654 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
660 return onValidated(packet.shared_from_this());
662 return onValidationFailed(packet.shared_from_this(),
"Sha256 Signature cannot be verified!");
666 switch (signature.getType()) {
669 if (!signature.hasKeyLocator()) {
670 return onValidationFailed(packet.shared_from_this(),
671 "Missing KeyLocator in SignatureInfo");
676 return onValidationFailed(packet.shared_from_this(),
"Unsupported signature type");
679 catch (
const KeyLocator::Error& e) {
680 return onValidationFailed(packet.shared_from_this(),
681 "Cannot decode KeyLocator in public key signature");
683 catch (
const tlv::Error& e) {
684 return onValidationFailed(packet.shared_from_this(),
"Cannot decode public key signature");
688 return onValidationFailed(packet.shared_from_this(),
"Unsupported KeyLocator type");
691 const Name& keyLocatorName = signature.getKeyLocator().getName();
693 shared_ptr<const v1::Certificate> trustedCert;
697 AnchorList::const_iterator it = m_anchors.find(keyLocatorName);
698 if (m_anchors.end() == it && m_certificateCache !=
nullptr)
699 trustedCert = m_certificateCache->getCertificate(keyLocatorName);
700 else if (m_anchors.end() != it)
701 trustedCert = it->second;
703 if (trustedCert !=
nullptr) {
704 if (
verifySignature(packet, signature, trustedCert->getPublicKeyInfo()))
705 return onValidated(packet.shared_from_this());
707 return onValidationFailed(packet.shared_from_this(),
"Cannot verify signature");
710 if (m_stepLimit == nSteps)
711 return onValidationFailed(packet.shared_from_this(),
"Maximum steps of validation reached");
714 bind(&ValidatorConfig::onCertValidated<Packet, OnValidated, OnFailed>,
715 this, _1, packet.shared_from_this(), onValidated, onValidationFailed);
718 bind(&ValidatorConfig::onCertFailed<Packet, OnFailed>,
719 this, _1, _2, packet.shared_from_this(), onValidationFailed);
721 Interest certInterest(keyLocatorName);
723 uint64_t incomingFaceId = 0;
724 auto incomingFaceIdTag = packet.template getTag<lp::IncomingFaceIdTag>();
725 if (incomingFaceIdTag !=
nullptr) {
726 incomingFaceId = incomingFaceIdTag->get();
728 auto nextStep = make_shared<ValidationRequest>(certInterest,
730 onCertValidationFailed,
734 nextSteps.push_back(nextStep);
737 return onValidationFailed(packet.shared_from_this(),
"Unsupported Signature Type");
740 template<
class Packet,
class OnVal
idated,
class OnFailed>
742 ValidatorConfig::onCertValidated(
const shared_ptr<const Data>& signCertificate,
743 const shared_ptr<const Packet>& packet,
744 const OnValidated& onValidated,
745 const OnFailed& onValidationFailed)
748 return onValidationFailed(packet,
749 "Cannot retrieve signer's cert: " +
750 signCertificate->getName().toUri());
752 shared_ptr<v1::IdentityCertificate> certificate;
754 certificate = make_shared<v1::IdentityCertificate>(*signCertificate);
756 catch (
const tlv::Error&) {
757 return onValidationFailed(packet,
758 "Cannot decode signer's cert: " +
759 signCertificate->getName().toUri());
762 if (!certificate->isTooLate() && !certificate->isTooEarly()) {
763 if (m_certificateCache !=
nullptr)
764 m_certificateCache->insertCertificate(certificate);
767 return onValidated(packet);
769 return onValidationFailed(packet,
770 "Cannot verify signature: " + packet->getName().toUri());
773 return onValidationFailed(packet,
774 "Signing certificate " +
775 signCertificate->getName().toUri() +
" is no longer valid.");
779 template<
class Packet,
class OnFailed>
781 ValidatorConfig::onCertFailed(
const shared_ptr<const Data>& signCertificate,
782 const std::string& failureInfo,
783 const shared_ptr<const Packet>& packet,
784 const OnFailed& onValidationFailed)
786 onValidationFailed(packet, failureInfo);
function< void(const shared_ptr< const Interest > &, const std::string &)> OnInterestValidationFailed
Callback to report a failed Interest validation.
static Name certificateNameToPublicKeyName(const Name &certificateName)
Get the public key name from the full certificate name.
const Name & getName() const
Copyright (c) 2013-2016 Regents of the University of California.
void load(const std::string &filename)
bool hasKeyLocator() const
Check if SignatureInfo block has a KeyLocator.
const KeyLocator & getKeyLocator() const
Get KeyLocator.
represents an Interest packet
indicates KeyLocator contains a Name
static time_point now() noexcept
ValidatorConfig(Face *face=nullptr, const shared_ptr< CertificateCache > &certificateCache=DEFAULT_CERTIFICATE_CACHE, const time::milliseconds &graceInterval=DEFAULT_GRACE_INTERVAL, const size_t stepLimit=10, const size_t maxTrackedKeys=1000, const time::system_clock::Duration &keyTimestampTtl=DEFAULT_KEY_TIMESTAMP_TTL)
function< void(const shared_ptr< const Data > &, const std::string &)> OnDataValidationFailed
Callback to report a failed Data validation.
static const time::milliseconds DEFAULT_GRACE_INTERVAL
function< void(const shared_ptr< const Data > &)> OnDataValidated
Callback to report a successful Data validation.
function< void(const shared_ptr< const Interest > &)> OnInterestValidated
Callback to report a successful Interest validation.
std::string toUri() const
Encode this name as a URI.
const Name & getName() const
get Name element
static const shared_ptr< CertificateCache > DEFAULT_CERTIFICATE_CACHE
provides the interfaces for packet validation.
Provide a communication channel with local or remote NDN forwarder.
size_t size() const
Get the number of components.
const size_t MIN_SIZE
minimal number of components for Command Interest
Name abstraction to represent an absolute name.
void checkPolicy(const Data &data, int nSteps, const OnDataValidated &onValidated, const OnDataValidationFailed &onValidationFailed, std::vector< shared_ptr< ValidationRequest >> &nextSteps) override
Check the Data against policy and return the next validation step if necessary.
const ssize_t POS_SIG_VALUE
boost::asio::io_service & getIoService()
const Signature & getSignature() const
boost::property_tree::ptree ConfigSection
const ssize_t POS_TIMESTAMP
system_clock::TimePoint fromUnixTimestamp(const milliseconds &duration)
Convert UNIX timestamp to system_clock::TimePoint.
const ssize_t POS_SIG_INFO
static const time::system_clock::Duration DEFAULT_KEY_TIMESTAMP_TTL
indicates content is a public key
static bool verifySignature(const Data &data, const v1::PublicKey &publicKey)
Verify the data using the publicKey.
uint64_t toNumber() const
Interpret this name component as nonNegativeInteger.
const Component & at(ssize_t i) const
Get component at the specified index.
represents an error in TLV encoding or decoding
A Signature is storage for the signature-related information (info and value) in a Data packet...