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