blob: 57c9c824665df1615dea31ba94fe3bc25c3a1dce [file] [log] [blame]
Junxiao Shidc2d6d22016-08-04 14:30:23 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev70244f42017-01-04 12:47:12 -08003 * Copyright (c) 2013-2017 Regents of the University of California.
Junxiao Shidc2d6d22016-08-04 14:30:23 +00004 *
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 "command-interest-validator.hpp"
Junxiao Shidc2d6d22016-08-04 14:30:23 +000023#include <boost/lexical_cast.hpp>
24
25namespace ndn {
26namespace security {
27
28std::ostream&
29operator<<(std::ostream& os, CommandInterestValidator::ErrorCode error)
30{
31 switch (error) {
32 case CommandInterestValidator::ErrorCode::NONE:
33 return os << "OK";
34 case CommandInterestValidator::ErrorCode::NAME_TOO_SHORT:
35 return os << "command Interest name is too short";
36 case CommandInterestValidator::ErrorCode::BAD_TIMESTAMP:
37 return os << "cannot parse timestamp";
38 case CommandInterestValidator::ErrorCode::BAD_SIG_INFO:
39 return os << "cannot parse SignatureInfo";
40 case CommandInterestValidator::ErrorCode::MISSING_KEY_LOCATOR:
41 return os << "KeyLocator is missing";
42 case CommandInterestValidator::ErrorCode::BAD_KEY_LOCATOR_TYPE:
43 return os << "KeyLocator type is not Name";
44 case CommandInterestValidator::ErrorCode::BAD_CERT_NAME:
45 return os << "cannot parse certificate name";
46 case CommandInterestValidator::ErrorCode::TIMESTAMP_OUT_OF_GRACE:
47 return os << "timestamp is out of grace period";
48 case CommandInterestValidator::ErrorCode::TIMESTAMP_REORDER:
49 return os << "timestamp is less than or equal to last timestamp";
50 }
51 return os;
52}
53
54static void
55invokeReject(const OnInterestValidationFailed& reject, const Interest& interest,
56 CommandInterestValidator::ErrorCode error)
57{
58 reject(interest.shared_from_this(), boost::lexical_cast<std::string>(error));
59}
60
61CommandInterestValidator::CommandInterestValidator(unique_ptr<Validator> inner,
62 const Options& options)
63 : m_inner(std::move(inner))
64 , m_options(options)
65 , m_index(m_container.get<0>())
66 , m_queue(m_container.get<1>())
67{
68 if (m_inner == nullptr) {
69 BOOST_THROW_EXCEPTION(std::invalid_argument("inner validator is nullptr"));
70 }
71
72 m_options.gracePeriod = std::max(m_options.gracePeriod, time::nanoseconds::zero());
73}
74
75void
76CommandInterestValidator::checkPolicy(const Interest& interest, int nSteps,
77 const OnInterestValidated& accept,
78 const OnInterestValidationFailed& reject,
79 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
80{
81 BOOST_ASSERT(nSteps == 0);
82 this->cleanup();
83
84 Name keyName;
85 uint64_t timestamp;
86 ErrorCode res = this->parseCommandInterest(interest, keyName, timestamp);
87 if (res != ErrorCode::NONE) {
88 return invokeReject(reject, interest, res);
89 }
90
91 time::system_clock::TimePoint receiveTime = time::system_clock::now();
92
93 m_inner->validate(interest,
94 [=] (const shared_ptr<const Interest>& interest) {
95 ErrorCode res = this->checkTimestamp(keyName, timestamp, receiveTime);
96 if (res != ErrorCode::NONE) {
97 return invokeReject(reject, *interest, res);
98 }
99 accept(interest);
100 }, reject);
101}
102
103void
104CommandInterestValidator::cleanup()
105{
106 time::steady_clock::TimePoint expiring = time::steady_clock::now() - m_options.timestampTtl;
107
108 while ((!m_queue.empty() && m_queue.front().lastRefreshed <= expiring) ||
109 (m_options.maxTimestamps >= 0 &&
110 m_queue.size() > static_cast<size_t>(m_options.maxTimestamps))) {
111 m_queue.pop_front();
112 }
113}
114
115CommandInterestValidator::ErrorCode
116CommandInterestValidator::parseCommandInterest(const Interest& interest, Name& keyName,
117 uint64_t& timestamp) const
118{
119 const Name& name = interest.getName();
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800120 if (name.size() < command_interest::MIN_SIZE) {
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000121 return ErrorCode::NAME_TOO_SHORT;
122 }
123
Alexander Afanasyev70244f42017-01-04 12:47:12 -0800124 const name::Component& timestampComp = name.at(command_interest::POS_TIMESTAMP);
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000125 if (!timestampComp.isNumber()) {
126 return ErrorCode::BAD_TIMESTAMP;
127 }
128 timestamp = timestampComp.toNumber();
129
130 SignatureInfo sig;
131 try {
132 sig.wireDecode(name[signed_interest::POS_SIG_INFO].blockFromValue());
133 }
134 catch (const tlv::Error&) {
135 return ErrorCode::BAD_SIG_INFO;
136 }
137
138 if (!sig.hasKeyLocator()) {
139 return ErrorCode::MISSING_KEY_LOCATOR;
140 }
141
142 const KeyLocator& keyLocator = sig.getKeyLocator();
143 if (keyLocator.getType() != KeyLocator::KeyLocator_Name) {
144 return ErrorCode::BAD_KEY_LOCATOR_TYPE;
145 }
146
147 try {
Alexander Afanasyev80782e02017-01-04 13:16:54 -0800148 v2::extractIdentityFromKeyName(keyLocator.getName());
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000149 }
Alexander Afanasyev80782e02017-01-04 13:16:54 -0800150 catch (const std::invalid_argument&) {
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000151 return ErrorCode::BAD_CERT_NAME;
152 }
153
Alexander Afanasyev80782e02017-01-04 13:16:54 -0800154 keyName = keyLocator.getName();
155
Junxiao Shidc2d6d22016-08-04 14:30:23 +0000156 return ErrorCode::NONE;
157}
158
159CommandInterestValidator::ErrorCode
160CommandInterestValidator::checkTimestamp(const Name& keyName, uint64_t timestamp,
161 time::system_clock::TimePoint receiveTime)
162{
163 time::steady_clock::TimePoint now = time::steady_clock::now();
164
165 // try to insert new record
166 Queue::iterator i = m_queue.end();
167 bool isNew = false;
168 std::tie(i, isNew) = m_queue.push_back({keyName, timestamp, now});
169
170 if (isNew) {
171 // check grace period
172 time::system_clock::TimePoint sigTime = time::fromUnixTimestamp(time::milliseconds(timestamp));
173 if (time::abs(sigTime - receiveTime) > m_options.gracePeriod) {
174 // out of grace period, delete new record
175 m_queue.erase(i);
176 return ErrorCode::TIMESTAMP_OUT_OF_GRACE;
177 }
178 }
179 else {
180 BOOST_ASSERT(i->keyName == keyName);
181
182 // compare timestamp with last timestamp
183 if (timestamp <= i->timestamp) {
184 return ErrorCode::TIMESTAMP_REORDER;
185 }
186
187 // set lastRefreshed field, and move to queue tail
188 m_queue.erase(i);
189 isNew = m_queue.push_back({keyName, timestamp, now}).second;
190 BOOST_ASSERT(isNew);
191 }
192
193 return ErrorCode::NONE;
194}
195
196void
197CommandInterestValidator::checkPolicy(const Data& data, int nSteps,
198 const OnDataValidated& accept,
199 const OnDataValidationFailed& reject,
200 std::vector<shared_ptr<ValidationRequest>>& nextSteps)
201{
202 BOOST_ASSERT(nSteps == 0);
203 m_inner->validate(data, accept, reject);
204}
205
206} // namespace security
207} // namespace ndn