blob: b18fc75f233b7e8de910b018a14eaf854cb7cb6c [file] [log] [blame]
Alexander Afanasyev7e721412017-01-11 13:36:08 -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 "validator.hpp"
23
24#include "face.hpp"
25#include "security/transform/public-key.hpp"
26#include "util/logger.hpp"
27
28namespace ndn {
29namespace security {
30namespace v2 {
31
32NDN_LOG_INIT(ndn.security.v2.Validator);
33
34#define NDN_LOG_DEBUG_DEPTH(x) NDN_LOG_DEBUG(std::string(state->getDepth() + 1, '>') << " " << x)
35#define NDN_LOG_TRACE_DEPTH(x) NDN_LOG_TRACE(std::string(state->getDepth() + 1, '>') << " " << x)
36
37Validator::Validator(unique_ptr<ValidationPolicy> policy, Face* face)
38 : m_policy(std::move(policy))
39 , m_face(face)
40 , m_verifiedCertificateCache(time::hours(1))
41 , m_unverifiedCertificateCache(time::minutes(5))
42 , m_maxDepth(25)
43{
44}
45
46Validator::~Validator() = default;
47
48void
49Validator::setMaxDepth(size_t depth)
50{
51 m_maxDepth = depth;
52}
53
54size_t
55Validator::getMaxDepth() const
56{
57 return m_maxDepth;
58}
59
60void
61Validator::validate(const Data& data,
62 const DataValidationSuccessCallback& successCb,
63 const DataValidationFailureCallback& failureCb)
64{
65 auto state = make_shared<DataValidationState>(data, successCb, failureCb);
66 NDN_LOG_DEBUG_DEPTH("Start validating data " << data.getName());
67
68 m_policy->checkPolicy(data, state,
69 [this] (const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state) {
70 if (certRequest == nullptr) {
71 state->bypassValidation();
72 }
73 else {
74 // need to fetch key and validate it
75 requestCertificate(certRequest, state);
76 }
77 });
78}
79
80void
81Validator::validate(const Interest& interest,
82 const InterestValidationSuccessCallback& successCb,
83 const InterestValidationFailureCallback& failureCb)
84{
85 auto state = make_shared<InterestValidationState>(interest, successCb, failureCb);
86 NDN_LOG_DEBUG_DEPTH("Start validating interest " << interest.getName());
87
88 m_policy->checkPolicy(interest, state,
89 [this] (const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state) {
90 if (certRequest == nullptr) {
91 state->bypassValidation();
92 }
93 else {
94 // need to fetch key and validate it
95 requestCertificate(certRequest, state);
96 }
97 });
98}
99
100void
101Validator::validate(const Certificate& cert, const shared_ptr<ValidationState>& state)
102{
103 NDN_LOG_DEBUG_DEPTH("Start validating certificate " << cert.getName());
104 m_policy->checkPolicy(cert, state,
105 [this, cert] (const shared_ptr<CertificateRequest>& certRequest, const shared_ptr<ValidationState>& state) {
106 if (certRequest == nullptr) {
107 state->fail({ValidationError::POLICY_ERROR, "Validation policy is not allowed to designate `" +
108 cert.getName().toUri() + "` as a trust anchor"});
109 }
110 else {
111 // need to fetch key and validate it
112 state->addCertificate(cert);
113 requestCertificate(certRequest, state);
114 }
115 });
116}
117
118const Certificate*
119Validator::findTrustedCert(const Interest& interestForCertificate, const shared_ptr<ValidationState>& state)
120{
121 auto anchor = m_trustAnchors.find(interestForCertificate);
122 if (anchor != nullptr) {
123 NDN_LOG_TRACE_DEPTH("Found certificate in anchor cache " << anchor->getName());
124 return anchor;
125 }
126
127 auto key = m_verifiedCertificateCache.find(interestForCertificate);
128 if (key != nullptr) {
129 NDN_LOG_TRACE_DEPTH("Found certificate in verified key cache " << key->getName());
130 return key;
131 }
132 return nullptr;
133}
134
135void
136Validator::requestCertificate(const shared_ptr<CertificateRequest>& certRequest,
137 const shared_ptr<ValidationState>& state)
138{
139 // TODO configurable check for the maximum number of steps
140 if (state->getDepth() >= m_maxDepth) {
141 state->fail({ValidationError::Code::EXCEEDED_DEPTH_LIMIT,
142 "Exceeded validation depth limit (" + to_string(m_maxDepth) + ")"});
143 return;
144 }
145
146 NDN_LOG_DEBUG_DEPTH("Retrieving " << certRequest->m_interest.getName());
147
148 // Check the trusted cache
149 auto cert = findTrustedCert(certRequest->m_interest, state);
150 if (cert != nullptr) {
151 cert = state->verifyCertificateChain(*cert);
152 if (cert != nullptr) {
153 state->verifyOriginalPacket(*cert);
154 }
155 for (auto trustedCert = std::make_move_iterator(state->m_certificateChain.begin());
156 trustedCert != std::make_move_iterator(state->m_certificateChain.end());
157 ++trustedCert) {
158 cacheVerifiedCertificate(*trustedCert);
159 }
160 return;
161 }
162
163 if (state->hasSeenCertificateName(certRequest->m_interest.getName())) {
164 state->fail({ValidationError::Code::LOOP_DETECTED,
165 "Loop detected at " + certRequest->m_interest.getName().toUri()});
166 return;
167 }
168
169 // Check untrusted cache
170 cert = m_unverifiedCertificateCache.find(certRequest->m_interest);
171 if (cert != nullptr) {
172 NDN_LOG_DEBUG_DEPTH("Found certificate in **un**verified key cache " << cert->getName());
173 return dataCallback(*cert, certRequest, state, false); // to avoid caching the cached key
174 }
175
176 // Attempt to retrieve certificate from the network
177 fetchCertificateFromNetwork(certRequest, state);
178}
179
180void
181Validator::fetchCertificateFromNetwork(const shared_ptr<CertificateRequest>& certRequest,
182 const shared_ptr<ValidationState>& state)
183{
184 if (m_face == nullptr) {
185 state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate in offline mode "
186 "`" + certRequest->m_interest.getName().toUri() + "`"});
187 return;
188 }
189
190 m_face->expressInterest(certRequest->m_interest,
191 [=] (const Interest& interest, const Data& data) {
192 dataCallback(data, certRequest, state);
193 },
194 [=] (const Interest& interest, const lp::Nack& nack) {
195 nackCallback(nack, certRequest, state);
196 },
197 [=] (const Interest& interest) {
198 timeoutCallback(certRequest, state);
199 });
200}
201
202void
203Validator::dataCallback(const Data& data,
204 const shared_ptr<CertificateRequest>& certRequest,
205 const shared_ptr<ValidationState>& state,
206 bool isFromNetwork)
207{
208 NDN_LOG_DEBUG_DEPTH("Retrieved certificate " << (isFromNetwork ? "from network " : "from cache ") << data.getName());
209
210 Certificate cert;
211 try {
212 cert = Certificate(data);
213 }
214 catch (const tlv::Error& e) {
215 return state->fail({ValidationError::Code::MALFORMED_CERT, "Retrieved a malformed certificate "
216 "`" + data.getName().toUri() + "` (" + e.what() + ")"});
217 }
218
219 if (!cert.isValid()) {
220 return state->fail({ValidationError::Code::EXPIRED_CERT, "Retrieved certificate is not yet "
221 "valid or has expired `" + cert.getName().toUri() + "`"});
222 }
223 if (isFromNetwork) {
224 cacheUnverifiedCertificate(Certificate(cert));
225 }
226 return validate(cert, state); // recursion step
227}
228
229void
230Validator::nackCallback(const lp::Nack& nack, const shared_ptr<CertificateRequest>& certRequest,
231 const shared_ptr<ValidationState>& state)
232{
233 NDN_LOG_DEBUG_DEPTH("NACK (" << nack.getReason() << ") while retrieving certificate "
234 << certRequest->m_interest.getName());
235
236 --certRequest->m_nRetriesLeft;
237 if (certRequest->m_nRetriesLeft > 0) {
238 // TODO implement delay for the the next fetch
239 fetchCertificateFromNetwork(certRequest, state);
240 }
241 else {
242 state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
243 "retries `" + certRequest->m_interest.getName().toUri() + "`"});
244 }
245}
246
247void
248Validator::timeoutCallback(const shared_ptr<CertificateRequest>& certRequest,
249 const shared_ptr<ValidationState>& state)
250{
251 NDN_LOG_DEBUG_DEPTH("Timeout while retrieving certificate "
252 << certRequest->m_interest.getName() << ", retrying");
253
254 --certRequest->m_nRetriesLeft;
255 if (certRequest->m_nRetriesLeft > 0) {
256 fetchCertificateFromNetwork(certRequest, state);
257 }
258 else {
259 state->fail({ValidationError::Code::CANNOT_RETRIEVE_CERT, "Cannot fetch certificate after all "
260 "retries `" + certRequest->m_interest.getName().toUri() + "`"});
261 }
262}
263
264////////////////////////////////////////////////////////////////////////
265// Trust anchor management
266////////////////////////////////////////////////////////////////////////
267
268void
269Validator::loadAnchor(const std::string& groupId, Certificate&& cert)
270{
271 m_trustAnchors.insert(groupId, std::move(cert));
272}
273
274void
275Validator::loadAnchor(const std::string& groupId, const std::string& certfilePath,
276 time::nanoseconds refreshPeriod, bool isDir)
277{
278 m_trustAnchors.insert(groupId, certfilePath, refreshPeriod, isDir);
279}
280
281void
282Validator::cacheVerifiedCertificate(Certificate&& cert)
283{
284 m_verifiedCertificateCache.insert(std::move(cert));
285}
286
287void
288Validator::cacheUnverifiedCertificate(Certificate&& cert)
289{
290 m_unverifiedCertificateCache.insert(std::move(cert));
291}
292
293const TrustAnchorContainer&
294Validator::getTrustAnchors() const
295{
296 return m_trustAnchors;
297}
298
299const CertificateCache&
300Validator::getVerifiedCertificateCache() const
301{
302 return m_verifiedCertificateCache;
303}
304
305const CertificateCache&
306Validator::getUnverifiedCertificateCache() const
307{
308 return m_unverifiedCertificateCache;
309}
310
311bool
312Validator::isCertificateCached(const Name& certName) const
313{
314 return (getVerifiedCertificateCache().find(certName) != nullptr ||
315 getVerifiedCertificateCache().find(certName) != nullptr);
316}
317
318} // namespace v2
319} // namespace security
320} // namespace ndn