blob: 53f6778f2367dc6fc106c543712d0e1ef6ba36af [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");
Davide Pesavento0a6456c2019-11-14 00:33:11 -050059 this->m_keyChain.sign(i, signingByIdentity(this->identity));
60 VALIDATE_FAILURE(i, "Empty policy should reject everything");
61}
62
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -080063template<typename Packet>
64class PacketName;
65
66template<>
67class PacketName<Interest>
68{
69public:
70 static std::string
71 getName()
72 {
73 return "interest";
74 }
75};
76
77template<>
78class PacketName<Data>
79{
80public:
81 static std::string
82 getName()
83 {
84 return "data";
85 }
86};
87
88template<typename PacketType>
89class ValidationPolicyConfigFixture : public HierarchicalValidatorFixture<ValidationPolicyConfig>
90{
91public:
92 ValidationPolicyConfigFixture()
93 : path(boost::filesystem::path(UNIT_TEST_CONFIG_PATH) / "security" / "v2" / "validation-policy-config")
94 {
95 boost::filesystem::create_directories(path);
96 baseConfig = R"CONF(
97 rule
98 {
99 id test-rule-id
100 for )CONF" + PacketName<Packet>::getName() + R"CONF(
101 filter
102 {
103 type name
104 name )CONF" + identity.getName().toUri() + R"CONF(
105 relation is-prefix-of
106 }
107 checker
108 {
109 type hierarchical
110 sig-type rsa-sha256
111 }
112 }
113 )CONF";
114 }
115
116 ~ValidationPolicyConfigFixture()
117 {
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500118 boost::system::error_code ec;
119 boost::filesystem::remove_all(path, ec);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800120 }
121
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500122protected:
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800123 using Packet = PacketType;
124
125 const boost::filesystem::path path;
126 std::string baseConfig;
127};
128
129template<typename PacketType>
130class LoadStringWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
131{
132public:
133 LoadStringWithFileAnchor()
134 {
135 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
136
137 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
138 this->policy.load(this->baseConfig + R"CONF(
139 trust-anchor
140 {
141 type file
142 file-name "trust-anchor.ndncert"
143 }
144 )CONF", (this->path / "test-config").string());
145
146 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
147 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
148 }
149};
150
151template<typename PacketType>
152class LoadFileWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
153{
154public:
155 LoadFileWithFileAnchor()
156 {
157 std::string configFile = (this->path / "config.conf").string();
158 {
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500159 std::ofstream config(configFile);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800160 config << this->baseConfig << R"CONF(
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500161 trust-anchor
162 {
163 type file
164 file-name "trust-anchor.ndncert"
165 }
166 )CONF";
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800167 }
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500168
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800169 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
170
171 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
172
173 this->policy.load(configFile);
174
175 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
176 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
177 }
178};
179
180template<typename PacketType>
181class LoadSectionWithFileAnchor : public ValidationPolicyConfigFixture<PacketType>
182{
183public:
184 LoadSectionWithFileAnchor()
185 {
186 auto section = makeSection(this->baseConfig + R"CONF(
187 trust-anchor
188 {
189 type file
190 file-name "trust-anchor.ndncert"
191 }
192 )CONF");
193
194 this->saveCertificate(this->identity, (this->path / "identity.ndncert").string());
195
196 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
197
198 this->policy.load(section, (this->path / "test-config").string());
199
200 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
201 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
202 }
203};
204
205template<typename PacketType>
206class LoadStringWithBase64Anchor : public ValidationPolicyConfigFixture<PacketType>
207{
208public:
209 LoadStringWithBase64Anchor()
210 {
211 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
212
213 std::ostringstream os;
Davide Pesavento0a6456c2019-11-14 00:33:11 -0500214 {
215 using namespace ndn::security::transform;
216 const auto& cert = this->identity.getDefaultKey().getDefaultCertificate().wireEncode();
217 bufferSource(cert.wire(), cert.size()) >> base64Encode(false) >> streamSink(os);
218 }
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800219
220 this->policy.load(this->baseConfig + R"CONF(
221 trust-anchor
222 {
223 type base64
224 base64-string ")CONF" + os.str() + R"CONF("
225 }
226 )CONF", (this->path / "test-config").string());
227
228 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
229 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
230 }
231};
232
233class NoRefresh
234{
235public:
236 static std::string
237 getRefreshString()
238 {
239 return "";
240 }
241};
242
243class Refresh1h
244{
245public:
246 static std::string
247 getRefreshString()
248 {
249 return "refresh 1h";
250 }
251
252 static time::milliseconds
253 getRefreshTime()
254 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500255 return 1_h;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800256 }
257};
258
259class Refresh1m
260{
261public:
262 static std::string
263 getRefreshString()
264 {
265 return "refresh 1m";
266 }
267
268 static time::milliseconds
269 getRefreshTime()
270 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500271 return 1_min;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800272 }
273};
274
275class Refresh1s
276{
277public:
278 static std::string
279 getRefreshString()
280 {
281 return "refresh 1s";
282 }
283
284 static time::milliseconds
285 getRefreshTime()
286 {
Davide Pesavento0f830802018-01-16 23:58:58 -0500287 return 1_s;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800288 }
289};
290
291template<typename PacketType, typename Refresh = NoRefresh>
292class LoadStringWithDirAnchor : public ValidationPolicyConfigFixture<PacketType>
293{
294public:
295 LoadStringWithDirAnchor()
296 {
297 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
298
299 boost::filesystem::create_directories(this->path / "keys");
300 this->saveCertificate(this->identity, (this->path / "keys" / "identity.ndncert").string());
301
302 this->policy.load(this->baseConfig + R"CONF(
303 trust-anchor
304 {
305 type dir
306 dir keys
307 )CONF" + Refresh::getRefreshString() + R"CONF(
308 }
309 )CONF", (this->path / "test-config").string());
310
311 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
312 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
313 }
314};
315
316using DataPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Data>,
317 LoadFileWithFileAnchor<Data>,
318 LoadSectionWithFileAnchor<Data>,
319 LoadStringWithBase64Anchor<Data>,
320 LoadStringWithDirAnchor<Data>,
321 LoadStringWithDirAnchor<Data, Refresh1h>,
322 LoadStringWithDirAnchor<Data, Refresh1m>,
323 LoadStringWithDirAnchor<Data, Refresh1s>
324 >;
325
326using InterestPolicies = boost::mpl::vector<LoadStringWithFileAnchor<Interest>,
327 LoadFileWithFileAnchor<Interest>,
328 LoadSectionWithFileAnchor<Interest>,
329 LoadStringWithBase64Anchor<Interest>,
330 LoadStringWithDirAnchor<Interest>,
331 LoadStringWithDirAnchor<Interest, Refresh1h>,
332 LoadStringWithDirAnchor<Interest, Refresh1m>,
333 LoadStringWithDirAnchor<Interest, Refresh1s>
334 >;
335
336BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateData, Policy, DataPolicies, Policy)
337{
338 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
339 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
340
341 using Packet = typename Policy::Packet;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400342 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800343
344 Packet packet = unsignedPacket;
345 VALIDATE_FAILURE(packet, "Unsigned");
346
347 packet = unsignedPacket;
348 this->m_keyChain.sign(packet, signingWithSha256());
349 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
350
351 packet = unsignedPacket;
352 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
353 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
354
355 packet = unsignedPacket;
356 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
357 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the policy-compliant cert");
358
359 packet = unsignedPacket;
360 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
361 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
362
363 packet = unsignedPacket;
364 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
365 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
366}
367
368BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateInterest, Policy, InterestPolicies, Policy)
369{
370 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
371 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
372
373 using Packet = typename Policy::Packet;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400374 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800375
376 Packet packet = unsignedPacket;
377 VALIDATE_FAILURE(packet, "Unsigned");
378
379 packet = unsignedPacket;
380 this->m_keyChain.sign(packet, signingWithSha256());
381 VALIDATE_FAILURE(packet, "Policy doesn't accept Sha256Digest signature");
382
383 packet = unsignedPacket;
384 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
385 VALIDATE_SUCCESS(packet, "Should get accepted, as signed by the anchor");
386
387 packet = unsignedPacket;
388 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
389 VALIDATE_FAILURE(packet, "Should fail, as there is no matching rule for data");
390
391 packet = unsignedPacket;
392 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
393 VALIDATE_FAILURE(packet, "Should fail, as signed by the policy-violating cert");
394
395 packet = unsignedPacket;
396 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
397 VALIDATE_FAILURE(packet, "Should fail, because subSelfSignedIdentity is not a trust anchor");
398}
399
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400400BOOST_FIXTURE_TEST_CASE(Reload, HierarchicalValidatorFixture<ValidationPolicyConfig>)
401{
402 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, false);
403 this->policy.load(R"CONF(
404 rule
405 {
406 id test-rule-data-id
407 for data
408 filter
409 {
410 type name
411 name /foo/bar
412 relation is-prefix-of
413 }
414 checker
415 {
416 type hierarchical
417 sig-type rsa-sha256
418 }
419 }
420 rule
421 {
422 id test-rule-interest-id
423 for interest
424 filter
425 {
426 type name
427 name /foo/bar
428 relation is-prefix-of
429 }
430 checker
431 {
432 type hierarchical
433 sig-type rsa-sha256
434 }
435 }
436 trust-anchor
437 {
438 type dir
439 dir keys
440 refresh 1h
441 }
442 )CONF", "test-config");
443 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
444 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, false);
445 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 1);
446 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 1);
447
448 this->policy.load(R"CONF(
449 trust-anchor
450 {
451 type any
452 }
453 )CONF", "test-config");
454 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
455 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
456 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
457 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
458}
459
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800460using Packets = boost::mpl::vector<Interest, Data>;
461
462BOOST_FIXTURE_TEST_CASE_TEMPLATE(TrustAnchorWildcard, Packet, Packets, ValidationPolicyConfigFixture<Packet>)
463{
464 this->policy.load(R"CONF(
465 trust-anchor
466 {
467 type any
468 }
469 )CONF", "test-config");
470
471 BOOST_CHECK_EQUAL(this->policy.m_isConfigured, true);
472 BOOST_CHECK_EQUAL(this->policy.m_shouldBypass, true);
473 BOOST_CHECK_EQUAL(this->policy.m_dataRules.size(), 0);
474 BOOST_CHECK_EQUAL(this->policy.m_interestRules.size(), 0);
475
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400476 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800477
478 Packet packet = unsignedPacket;
479 VALIDATE_SUCCESS(packet, "Policy should accept everything");
480
481 packet = unsignedPacket;
482 this->m_keyChain.sign(packet, signingWithSha256());
483 VALIDATE_SUCCESS(packet, "Policy should accept everything");
484
485 packet = unsignedPacket;
486 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
487 VALIDATE_SUCCESS(packet, "Policy should accept everything");
488
489 packet = unsignedPacket;
490 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
491 VALIDATE_SUCCESS(packet, "Policy should accept everything");
492
493 packet = unsignedPacket;
494 this->m_keyChain.sign(packet, signingByIdentity(this->otherIdentity));
495 VALIDATE_SUCCESS(packet, "Policy should accept everything");
496
497 packet = unsignedPacket;
498 this->m_keyChain.sign(packet, signingByIdentity(this->subSelfSignedIdentity));
499 VALIDATE_SUCCESS(packet, "Policy should accept everything");
500}
501
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400502using RefreshPolicies = boost::mpl::vector<Refresh1h, Refresh1m, Refresh1s>;
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800503
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400504template<typename RefreshPolicy>
505class RefreshPolicyFixture : public LoadStringWithDirAnchor<Data, RefreshPolicy>
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800506{
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800507};
508
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400509BOOST_FIXTURE_TEST_CASE_TEMPLATE(ValidateRefresh, Refresh, RefreshPolicies, RefreshPolicyFixture<Refresh>)
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800510{
511 using Packet = Data;
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400512 Packet unsignedPacket("/Security/ValidatorFixture/Sub1/Sub2/Packet");
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800513
514 boost::filesystem::remove(this->path / "keys" / "identity.ndncert");
Alexander Afanasyev6aff0242017-08-29 17:14:44 -0400515 this->advanceClocks(Refresh::getRefreshTime(), 3);
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800516
517 Packet packet = unsignedPacket;
518 this->m_keyChain.sign(packet, signingByIdentity(this->identity));
519 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
520
521 packet = unsignedPacket;
522 this->m_keyChain.sign(packet, signingByIdentity(this->subIdentity));
523 VALIDATE_FAILURE(packet, "Should fail, as the trust anchor should no longer exist");
524}
525
Alexander Afanasyev7b112462018-10-17 11:51:52 -0400526BOOST_FIXTURE_TEST_CASE(OrphanedPolicyLoad, HierarchicalValidatorFixture<ValidationPolicyConfig>) // Bug #4758
527{
528 ValidationPolicyConfig policy1;
529 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
530
531 // Reloading would have triggered a segfault
532 BOOST_CHECK_THROW(policy1.load("trust-anchor { type any }", "test-config"), Error);
533
534 ValidationPolicyConfig policy2;
535
536 std::string config = R"CONF(
537 trust-anchor
538 {
539 type dir
540 dir keys
541 refresh 1h
542 }
543 )CONF";
544
545 // Inserting trust anchor would have triggered a segfault
546 BOOST_CHECK_THROW(policy2.load(config, "test-config"), Error);
547}
548
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800549BOOST_AUTO_TEST_SUITE_END() // TestValidationPolicyConfig
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800550BOOST_AUTO_TEST_SUITE_END() // Security
551
552} // namespace tests
553} // namespace validator_config
Alexander Afanasyev09236c22020-06-03 13:42:38 -0400554} // inline namespace v2
Alexander Afanasyeve5a19b82017-01-30 22:30:46 -0800555} // namespace security
556} // namespace ndn