blob: e660598a9a5e747c72b9bd5b231ef9f42b1123e9 [file] [log] [blame]
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
Alexander Afanasyev09236c22020-06-03 13:42:38 -04003 * Copyright (c) 2013-2020 Regents of the University of California.
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -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
Alexander Afanasyev09236c22020-06-03 13:42:38 -040022#include "ndn-cxx/security/validation-policy-config.hpp"
Davide Pesavento7e780642018-11-24 15:51:34 -050023#include "ndn-cxx/security/transform/base64-encode.hpp"
24#include "ndn-cxx/security/transform/buffer-source.hpp"
25#include "ndn-cxx/security/transform/stream-sink.hpp"
26#include "ndn-cxx/util/logger.hpp"
27#include "ndn-cxx/util/io.hpp"
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080028
Davide Pesavento7e780642018-11-24 15:51:34 -050029#include "tests/boost-test.hpp"
Alexander Afanasyev09236c22020-06-03 13:42:38 -040030#include "tests/unit/security/validator-config/common.hpp"
31#include "tests/unit/security/validator-fixture.hpp"
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080032
33namespace ndn {
34namespace security {
Alexander Afanasyev09236c22020-06-03 13:42:38 -040035inline namespace v2 {
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080036namespace validator_config {
37namespace tests {
38
39using namespace ndn::tests;
40using namespace ndn::security::v2::tests;
41
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080042BOOST_AUTO_TEST_SUITE(Security)
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080043BOOST_AUTO_TEST_SUITE(TestValidationPolicyConfig)
44
Davide Pesavento0a6456c2019-11-14 00:33:11 -050045BOOST_FIXTURE_TEST_CASE(EmptyConfig, HierarchicalValidatorFixture<ValidationPolicyConfig>)
46{
47 this->policy.load(ConfigSection{}, "<empty>");
48
49 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
50 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
51 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
52 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
53
Alexander Afanasyev09236c22020-06-03 13:42:38 -040054 Data d("/Security/ValidationPolicyConfig/D");
Davide Pesavento0a6456c2019-11-14 00:33:11 -050055 this->m_keyChain.sign(d, signingByIdentity(this->identity));
56 VALIDATE_FAILURE(d, "Empty policy should reject everything");
57
Alexander Afanasyev09236c22020-06-03 13:42:38 -040058 Interest i("/Security/ValidationPolicyConfig/I");
Eric Newberryb74bbda2020-06-18 19:33:58 -070059 i.setCanBePrefix(false);
Davide Pesavento0a6456c2019-11-14 00:33:11 -050060 this->m_keyChain.sign(i, signingByIdentity(this->identity));
61 VALIDATE_FAILURE(i, "Empty policy should reject everything");
62}
63
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080064template<typename Packet>
65class PacketName;
66
67template<>
68class PacketName<Interest>
69{
70public:
71 static std::string
72 getName()
73 {
74 return "interest";
75 }
76};
77
78template<>
79class PacketName<Data>
80{
81public:
82 static std::string
83 getName()
84 {
85 return "data";
86 }
87};
88
89template<typename PacketType>
90class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
91{
92public:
93 ValidationPolicyConfigFixture()
94 : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
95 {
96 boost::filesystem::create_directories(path);
97 baseConfig = R"CONF(
98 rule
99 {
100 id test-rule-id
101 for )CONF" + PacketName<Packet>::getName() + R"CONF(
102 filter
103 {
104 type name
105 name )CONF" + identity.getName().toUri() + R"CONF(
106 relation is-prefix-of
107 }
108 checker
109 {
110 type hierarchical
111 sig-type rsa-sha256
112 }
113 }
114 )CONF";
115 }
116
117 ~ValidationPolicyConfigFixture()
118 {
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500119 boost::system::error_code ec;
120 boost::filesystem::remove_all(path, ec);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800121 }
122
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500123protected:
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800124 using Packet = PacketType;
125
126 const boost::filesystem::path path;
127 std::string baseConfig;
128};
129
130template<typename PacketType>
131class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
132{
133public:
134 LoadStringWithFileAnchor()
135 {
136 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
137
138 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
139 this->policy.load(this->baseConfig + R"CONF(
140 trust-anchor
141 {
142 type file
143 file-name "trust-anchor.ndncert"
144 }
145 )CONF", (this->path / "test-config").string());
146
147 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
148 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
149 }
150};
151
152template<typename PacketType>
153class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
154{
155public:
156 LoadFileWithFileAnchor()
157 {
158 std::string configFile = (this->path / "config.conf").string();
159 {
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500160 std::ofstream config(configFile);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800161 config << this->baseConfig << R"CONF(
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500162 trust-anchor
163 {
164 type file
165 file-name "trust-anchor.ndncert"
166 }
167 )CONF";
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800168 }
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500169
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800170 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
171
172 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
173
174 this->policy.load(configFile);
175
176 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
177 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
178 }
179};
180
181template<typename PacketType>
182class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
183{
184public:
185 LoadSectionWithFileAnchor()
186 {
187 auto section = makeSection(this->baseConfig + R"CONF(
188 trust-anchor
189 {
190 type file
191 file-name "trust-anchor.ndncert"
192 }
193 )CONF");
194
195 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
196
197 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
198
199 this->policy.load(section, (this->path / "test-config").string());
200
201 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
202 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
203 }
204};
205
206template<typename PacketType>
207class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
208{
209public:
210 LoadStringWithBase64Anchor()
211 {
212 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
213
214 std::ostringstream os;
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500215 {
216 using namespace ndn::security::transform;
217 const auto& cert = this->identity.getDefaultKey().getDefaultCertificate().wireEncode();
218 bufferSource(cert.wire(), cert.size()) >> base64Encode(false) >> streamSink(os);
219 }
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800220
221 this->policy.load(this->baseConfig + R"CONF(
222 trust-anchor
223 {
224 type base64
225 base64-string ")CONF" + os.str() + R"CONF("
226 }
227 )CONF", (this->path / "test-config").string());
228
229 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
230 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
231 }
232};
233
234class NoRefresh
235{
236public:
237 static std::string
238 getRefreshString()
239 {
240 return "";
241 }
242};
243
244class Refresh1h
245{
246public:
247 static std::string
248 getRefreshString()
249 {
250 return "refresh 1h";
251 }
252
253 static time::milliseconds
254 getRefreshTime()
255 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500256 return 1_h;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800257 }
258};
259
260class Refresh1m
261{
262public:
263 static std::string
264 getRefreshString()
265 {
266 return "refresh 1m";
267 }
268
269 static time::milliseconds
270 getRefreshTime()
271 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500272 return 1_min;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800273 }
274};
275
276class Refresh1s
277{
278public:
279 static std::string
280 getRefreshString()
281 {
282 return "refresh 1s";
283 }
284
285 static time::milliseconds
286 getRefreshTime()
287 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500288 return 1_s;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800289 }
290};
291
292template<typename PacketType, typename Refresh = NoRefresh>
293class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
294{
295public:
296 LoadStringWithDirAnchor()
297 {
298 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
299
300 boost::filesystem::create_directories(this->path / "keys");
301 this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
302
303 this->policy.load(this->baseConfig + R"CONF(
304 trust-anchor
305 {
306 type dir
307 dir keys
308 )CONF" + Refresh::getRefreshString() + R"CONF(
309 }
310 )CONF", (this->path / "test-config").string());
311
312 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
313 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
314 }
315};
316
317using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
318 LoadFileWithFileAnchor<Data>,
319 LoadSectionWithFileAnchor<Data>,
320 LoadStringWithBase64Anchor<Data>,
321 LoadStringWithDirAnchor<Data>,
322 LoadStringWithDirAnchor<Data, Refresh1h>,
323 LoadStringWithDirAnchor<Data, Refresh1m>,
324 LoadStringWithDirAnchor<Data, Refresh1s>
325 >;
326
327using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
328 LoadFileWithFileAnchor<Interest>,
329 LoadSectionWithFileAnchor<Interest>,
330 LoadStringWithBase64Anchor<Interest>,
331 LoadStringWithDirAnchor<Interest>,
332 LoadStringWithDirAnchor<Interest, Refresh1h>,
333 LoadStringWithDirAnchor<Interest, Refresh1m>,
334 LoadStringWithDirAnchor<Interest, Refresh1s>
335 >;
336
337BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
338{
339 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
340 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
341
342 using Packet = typename Policy::Packet;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400343 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800344
345 Packet packet = unsignedPacket;
346 VALIDATE_FAILURE(packet, "Unsigned");
347
348 packet = unsignedPacket;
349 this->m_keyChain.sign(packet, signingWithSha256());
350 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
351
352 packet = unsignedPacket;
353 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
354 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
355
356 packet = unsignedPacket;
357 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
358 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
359
360 packet = unsignedPacket;
361 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
362 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
363
364 packet = unsignedPacket;
365 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
366 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
367}
368
369BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
370{
371 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
372 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
373
374 using Packet = typename Policy::Packet;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400375 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Eric Newberryb74bbda2020-06-18 19:33:58 -0700376 // All of the packet types inputed to this test case template are Interests, so we can call
377 // setCanBePrefix
378 unsignedPacket.setCanBePrefix(false);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800379
380 Packet packet = unsignedPacket;
381 VALIDATE_FAILURE(packet, "Unsigned");
382
383 packet = unsignedPacket;
384 this->m_keyChain.sign(packet, signingWithSha256());
385 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
386
387 packet = unsignedPacket;
388 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
389 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
390
391 packet = unsignedPacket;
392 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
393 VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
394
395 packet = unsignedPacket;
396 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
397 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
398
399 packet = unsignedPacket;
400 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
401 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
402}
403
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400404BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
405{
406 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
407 this->policy.load(R"CONF(
408 rule
409 {
410 id test-rule-data-id
411 for data
412 filter
413 {
414 type name
415 name /foo/bar
416 relation is-prefix-of
417 }
418 checker
419 {
420 type hierarchical
421 sig-type rsa-sha256
422 }
423 }
424 rule
425 {
426 id test-rule-interest-id
427 for interest
428 filter
429 {
430 type name
431 name /foo/bar
432 relation is-prefix-of
433 }
434 checker
435 {
436 type hierarchical
437 sig-type rsa-sha256
438 }
439 }
440 trust-anchor
441 {
442 type dir
443 dir keys
444 refresh 1h
445 }
446 )CONF", "test-config");
447 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
448 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
449 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
450 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
451
452 this->policy.load(R"CONF(
453 trust-anchor
454 {
455 type any
456 }
457 )CONF", "test-config");
458 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
459 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
460 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
461 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
462}
463
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800464using Packets = boost::mpl::vector<Interest, Data>;
465
466BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
467{
468 this->policy.load(R"CONF(
469 trust-anchor
470 {
471 type any
472 }
473 )CONF", "test-config");
474
475 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
476 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
477 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
478 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
479
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400480 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800481
482 Packet packet = unsignedPacket;
483 VALIDATE_SUCCESS(packet, "Policy should accept everything");
484
485 packet = unsignedPacket;
486 this->m_keyChain.sign(packet, signingWithSha256());
487 VALIDATE_SUCCESS(packet, "Policy should accept everything");
488
489 packet = unsignedPacket;
490 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
491 VALIDATE_SUCCESS(packet, "Policy should accept everything");
492
493 packet = unsignedPacket;
494 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
495 VALIDATE_SUCCESS(packet, "Policy should accept everything");
496
497 packet = unsignedPacket;
498 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
499 VALIDATE_SUCCESS(packet, "Policy should accept everything");
500
501 packet = unsignedPacket;
502 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
503 VALIDATE_SUCCESS(packet, "Policy should accept everything");
504}
505
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400506using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800507
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400508template<typename RefreshPolicy>
509class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800510{
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800511};
512
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400513BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800514{
515 using Packet = Data;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400516 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800517
518 boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400519 this->advanceClocks(Refresh::getRefreshTime(), 3);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800520
521 Packet packet = unsignedPacket;
522 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
523 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
524
525 packet = unsignedPacket;
526 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
527 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
528}
529
Alexander Afanasyev7b112462018-10-17 11:51:52 -0400530BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
531{
532 ValidationPolicyConfig policy1;
533 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
534
535 // Reloading would have triggered a segfault
536 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
537
538 ValidationPolicyConfig policy2;
539
540 std::string config = R"CONF(
541 trust-anchor
542 {
543 type dir
544 dir keys
545 refresh 1h
546 }
547 )CONF";
548
549 // Inserting trust anchor would have triggered a segfault
550 BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
551}
552
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800553BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800554BOOST_AUTO_TEST_SUITE_END() // Security
555
556} // namespace tests
557} // namespace validator_config
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400558} // inline namespace v2
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800559} // namespace security
560} // namespace ndn