blob: 7e04badba29d247893e48b6021fdbb711b697d03 [file] [log] [blame]
Alexander Afanasyev93338872017-01-30 22:37:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento5df42a82018-03-08 20:06:51 -05002/*
3 * Copyright (c) 2013-2018 Regents of the University of California.
Alexander Afanasyev93338872017-01-30 22:37:00 -08004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6 *
7 * 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.
20 */
21
22#include "validation-policy-command-interest.hpp"
Alexander Afanasyev93338872017-01-30 22:37:00 -080023
24namespace ndn {
25namespace security {
26namespace v2 {
27
28ValidationPolicyCommandInterest::ValidationPolicyCommandInterest(unique_ptr<ValidationPolicy> inner,
29 const Options& options)
30 : m_options(options)
31 , m_index(m_container.get<0>())
32 , m_queue(m_container.get<1>())
33{
34 if (inner == nullptr) {
35 BOOST_THROW_EXCEPTION(std::invalid_argument("inner policy is missing"));
36 }
37 setInnerPolicy(std::move(inner));
38
Davide Pesavento5df42a82018-03-08 20:06:51 -050039 m_options.gracePeriod = std::max(m_options.gracePeriod, 0_ns);
Alexander Afanasyev93338872017-01-30 22:37:00 -080040}
41
42void
43ValidationPolicyCommandInterest::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
44 const ValidationContinuation& continueValidation)
45{
46 getInnerPolicy().checkPolicy(data, state, continueValidation);
47}
48
49void
50ValidationPolicyCommandInterest::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
51 const ValidationContinuation& continueValidation)
52{
53 bool isOk = false;
54 Name keyName;
55 uint64_t timestamp = 0;
56 std::tie(isOk, keyName, timestamp) = parseCommandInterest(interest, state);
57 if (!isOk) {
58 return;
59 }
60
61 if (!checkTimestamp(state, keyName, timestamp)) {
62 return;
63 }
64 getInnerPolicy().checkPolicy(interest, state, std::bind(continueValidation, _1, _2));
65}
66
67void
68ValidationPolicyCommandInterest::cleanup()
69{
Davide Pesavento5df42a82018-03-08 20:06:51 -050070 auto expiring = time::steady_clock::now() - m_options.recordLifetime;
Alexander Afanasyev93338872017-01-30 22:37:00 -080071
72 while ((!m_queue.empty() && m_queue.front().lastRefreshed <= expiring) ||
73 (m_options.maxRecords >= 0 &&
74 m_queue.size() > static_cast<size_t>(m_options.maxRecords))) {
75 m_queue.pop_front();
76 }
77}
78
79std::tuple<bool, Name, uint64_t>
80ValidationPolicyCommandInterest::parseCommandInterest(const Interest& interest,
81 const shared_ptr<ValidationState>& state) const
82{
83 const Name& name = interest.getName();
84 if (name.size() < command_interest::MIN_SIZE) {
85 state->fail({ValidationError::POLICY_ERROR, "Command interest name `" +
86 interest.getName().toUri() + "` is too short"});
87 return std::make_tuple(false, Name(), 0);
88 }
89
90 const name::Component& timestampComp = name.at(command_interest::POS_TIMESTAMP);
91 if (!timestampComp.isNumber()) {
92 state->fail({ValidationError::POLICY_ERROR, "Command interest `" +
93 interest.getName().toUri() + "` doesn't include timestamp component"});
94 return std::make_tuple(false, Name(), 0);
95 }
96
Junxiao Shi830ba972017-06-23 22:44:41 +000097 Name klName = getKeyLocatorName(interest, *state);
98 if (!state->getOutcome()) { // already failed
Alexander Afanasyev93338872017-01-30 22:37:00 -080099 return std::make_tuple(false, Name(), 0);
100 }
101
Junxiao Shi830ba972017-06-23 22:44:41 +0000102 return std::make_tuple(true, klName, timestampComp.toNumber());
Alexander Afanasyev93338872017-01-30 22:37:00 -0800103}
104
105bool
106ValidationPolicyCommandInterest::checkTimestamp(const shared_ptr<ValidationState>& state,
107 const Name& keyName, uint64_t timestamp)
108{
109 this->cleanup();
110
Davide Pesavento5df42a82018-03-08 20:06:51 -0500111 auto now = time::system_clock::now();
112 auto timestampPoint = time::fromUnixTimestamp(time::milliseconds(timestamp));
Alexander Afanasyev93338872017-01-30 22:37:00 -0800113 if (timestampPoint < now - m_options.gracePeriod || timestampPoint > now + m_options.gracePeriod) {
Davide Pesavento5df42a82018-03-08 20:06:51 -0500114 state->fail({ValidationError::POLICY_ERROR,
115 "Timestamp is outside the grace period for key " + keyName.toUri()});
Alexander Afanasyev93338872017-01-30 22:37:00 -0800116 return false;
117 }
Davide Pesavento5df42a82018-03-08 20:06:51 -0500118
Alexander Afanasyev93338872017-01-30 22:37:00 -0800119 auto it = m_index.find(keyName);
120 if (it != m_index.end()) {
121 if (timestamp <= it->timestamp) {
Davide Pesavento5df42a82018-03-08 20:06:51 -0500122 state->fail({ValidationError::POLICY_ERROR,
123 "Timestamp is reordered for key " + keyName.toUri()});
Alexander Afanasyev93338872017-01-30 22:37:00 -0800124 return false;
125 }
126 }
127
Davide Pesavento5df42a82018-03-08 20:06:51 -0500128 auto interestState = dynamic_pointer_cast<InterestValidationState>(state);
129 BOOST_ASSERT(interestState != nullptr);
130 interestState->afterSuccess.connect([=] (const Interest&) { insertNewRecord(keyName, timestamp); });
Alexander Afanasyev93338872017-01-30 22:37:00 -0800131 return true;
132}
133
134void
Davide Pesavento5df42a82018-03-08 20:06:51 -0500135ValidationPolicyCommandInterest::insertNewRecord(const Name& keyName, uint64_t timestamp)
Alexander Afanasyev93338872017-01-30 22:37:00 -0800136{
137 // try to insert new record
Davide Pesavento5df42a82018-03-08 20:06:51 -0500138 auto now = time::steady_clock::now();
139 auto i = m_queue.end();
Alexander Afanasyev93338872017-01-30 22:37:00 -0800140 bool isNew = false;
141 LastTimestampRecord newRecord{keyName, timestamp, now};
142 std::tie(i, isNew) = m_queue.push_back(newRecord);
143
144 if (!isNew) {
145 BOOST_ASSERT(i->keyName == keyName);
146
147 // set lastRefreshed field, and move to queue tail
148 m_queue.erase(i);
149 isNew = m_queue.push_back(newRecord).second;
150 BOOST_VERIFY(isNew);
151 }
152}
153
154} // namespace v2
155} // namespace security
156} // namespace ndn