blob: 5753ef3d215df7e8a53d4062617d848dc13efbce [file] [log] [blame]
Alexander Afanasyev93338872017-01-30 22:37:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2017 Regents of the University of California.
4 *
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"
23#include "../pib/key.hpp"
24
25namespace ndn {
26namespace security {
27namespace v2 {
28
29ValidationPolicyCommandInterest::ValidationPolicyCommandInterest(unique_ptr<ValidationPolicy> inner,
30 const Options& options)
31 : m_options(options)
32 , m_index(m_container.get<0>())
33 , m_queue(m_container.get<1>())
34{
35 if (inner == nullptr) {
36 BOOST_THROW_EXCEPTION(std::invalid_argument("inner policy is missing"));
37 }
38 setInnerPolicy(std::move(inner));
39
40 m_options.gracePeriod = std::max(m_options.gracePeriod, time::nanoseconds::zero());
41}
42
43void
44ValidationPolicyCommandInterest::checkPolicy(const Data& data, const shared_ptr<ValidationState>& state,
45 const ValidationContinuation& continueValidation)
46{
47 getInnerPolicy().checkPolicy(data, state, continueValidation);
48}
49
50void
51ValidationPolicyCommandInterest::checkPolicy(const Interest& interest, const shared_ptr<ValidationState>& state,
52 const ValidationContinuation& continueValidation)
53{
54 bool isOk = false;
55 Name keyName;
56 uint64_t timestamp = 0;
57 std::tie(isOk, keyName, timestamp) = parseCommandInterest(interest, state);
58 if (!isOk) {
59 return;
60 }
61
62 if (!checkTimestamp(state, keyName, timestamp)) {
63 return;
64 }
65 getInnerPolicy().checkPolicy(interest, state, std::bind(continueValidation, _1, _2));
66}
67
68void
69ValidationPolicyCommandInterest::cleanup()
70{
71 time::steady_clock::TimePoint expiring = time::steady_clock::now() - m_options.recordLifetime;
72
73 while ((!m_queue.empty() && m_queue.front().lastRefreshed <= expiring) ||
74 (m_options.maxRecords >= 0 &&
75 m_queue.size() > static_cast<size_t>(m_options.maxRecords))) {
76 m_queue.pop_front();
77 }
78}
79
80std::tuple<bool, Name, uint64_t>
81ValidationPolicyCommandInterest::parseCommandInterest(const Interest& interest,
82 const shared_ptr<ValidationState>& state) const
83{
84 const Name& name = interest.getName();
85 if (name.size() < command_interest::MIN_SIZE) {
86 state->fail({ValidationError::POLICY_ERROR, "Command interest name `" +
87 interest.getName().toUri() + "` is too short"});
88 return std::make_tuple(false, Name(), 0);
89 }
90
91 const name::Component& timestampComp = name.at(command_interest::POS_TIMESTAMP);
92 if (!timestampComp.isNumber()) {
93 state->fail({ValidationError::POLICY_ERROR, "Command interest `" +
94 interest.getName().toUri() + "` doesn't include timestamp component"});
95 return std::make_tuple(false, Name(), 0);
96 }
97
98 SignatureInfo sig;
99 try {
100 sig.wireDecode(name[signed_interest::POS_SIG_INFO].blockFromValue());
101 }
102 catch (const tlv::Error&) {
103 state->fail({ValidationError::POLICY_ERROR, "Command interest `" +
104 interest.getName().toUri() + "` does not include SignatureInfo component"});
105 return std::make_tuple(false, Name(), 0);
106 }
107
108 if (!sig.hasKeyLocator()) {
109 state->fail({ValidationError::INVALID_KEY_LOCATOR, "Command interest `" +
110 interest.getName().toUri() + "` does not include KeyLocator"});
111 return std::make_tuple(false, Name(), 0);
112 }
113
114 const KeyLocator& keyLocator = sig.getKeyLocator();
115 if (keyLocator.getType() != KeyLocator::KeyLocator_Name) {
116 state->fail({ValidationError::INVALID_KEY_LOCATOR, "Command interest `" +
117 interest.getName().toUri() + "` KeyLocator type is not Name"});
118 return std::make_tuple(false, Name(), 0);
119 }
120
121 return std::make_tuple(true, keyLocator.getName(), timestampComp.toNumber());
122}
123
124bool
125ValidationPolicyCommandInterest::checkTimestamp(const shared_ptr<ValidationState>& state,
126 const Name& keyName, uint64_t timestamp)
127{
128 this->cleanup();
129
130 time::system_clock::TimePoint now = time::system_clock::now();
131 time::system_clock::TimePoint timestampPoint = time::fromUnixTimestamp(time::milliseconds(timestamp));
132 if (timestampPoint < now - m_options.gracePeriod || timestampPoint > now + m_options.gracePeriod) {
133 state->fail({ValidationError::POLICY_ERROR, "Timestamp is outside the grace period for key " + keyName.toUri()});
134 return false;
135 }
136 auto it = m_index.find(keyName);
137 if (it != m_index.end()) {
138 if (timestamp <= it->timestamp) {
139 state->fail({ValidationError::POLICY_ERROR, "Timestamp is reordered for key " + keyName.toUri()});
140 return false;
141 }
142 }
143
144 shared_ptr<InterestValidationState> interestState = std::dynamic_pointer_cast<InterestValidationState>(state);
145 interestState->afterSuccess.connect(bind(&ValidationPolicyCommandInterest::insertNewRecord,
146 this, _1, keyName, timestamp));
147 return true;
148}
149
150void
151ValidationPolicyCommandInterest::insertNewRecord(const Interest& interest, const Name& keyName,
152 uint64_t timestamp)
153{
154 // try to insert new record
155 time::steady_clock::TimePoint now = time::steady_clock::now();
156 Queue::iterator i = m_queue.end();
157 bool isNew = false;
158 LastTimestampRecord newRecord{keyName, timestamp, now};
159 std::tie(i, isNew) = m_queue.push_back(newRecord);
160
161 if (!isNew) {
162 BOOST_ASSERT(i->keyName == keyName);
163
164 // set lastRefreshed field, and move to queue tail
165 m_queue.erase(i);
166 isNew = m_queue.push_back(newRecord).second;
167 BOOST_VERIFY(isNew);
168 }
169}
170
171} // namespace v2
172} // namespace security
173} // namespace ndn