blob: 635ee121b5e52ddafa2e71cfdc3aadb2f00279b0 [file] [log] [blame]
Junxiao Shid7631272016-08-17 04:16:31 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shia9079802017-08-26 14:12:30 +00002/*
Davide Pesaventoa599d2a2022-02-16 18:52:43 -05003 * Copyright (c) 2014-2022, Regents of the University of California,
Junxiao Shid7631272016-08-17 04:16:31 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "mgmt/command-authenticator.hpp"
Junxiao Shid7631272016-08-17 04:16:31 +000027
Davide Pesavento78ddcab2019-02-28 22:00:03 -050028#include "manager-common-fixture.hpp"
Junxiao Shid7631272016-08-17 04:16:31 +000029
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040030namespace nfd::tests {
Junxiao Shid7631272016-08-17 04:16:31 +000031
Davide Pesavento20cafa82022-07-25 01:15:03 -040032class CommandAuthenticatorFixture : public InterestSignerFixture
Junxiao Shid7631272016-08-17 04:16:31 +000033{
34protected:
Junxiao Shid7631272016-08-17 04:16:31 +000035 void
36 makeModules(std::initializer_list<std::string> modules)
37 {
Davide Pesavento20cafa82022-07-25 01:15:03 -040038 for (const auto& module : modules) {
Junxiao Shid7631272016-08-17 04:16:31 +000039 authorizations.emplace(module, authenticator->makeAuthorization(module, "verb"));
40 }
41 }
42
43 void
44 loadConfig(const std::string& config)
45 {
Junxiao Shid7631272016-08-17 04:16:31 +000046 ConfigFile cf;
47 authenticator->setConfigFile(cf);
Davide Pesavento20cafa82022-07-25 01:15:03 -040048 cf.parse(config, false, "command-authenticator-test.conf");
Junxiao Shid7631272016-08-17 04:16:31 +000049 }
50
51 bool
52 authorize(const std::string& module, const Name& identity,
Davide Pesavento6d6f2072022-09-12 23:08:34 -040053 const std::function<void(Interest&)>& modifyInterest = nullptr,
54 ndn::security::SignedInterestFormat format = ndn::security::SignedInterestFormat::V02)
Junxiao Shid7631272016-08-17 04:16:31 +000055 {
Davide Pesavento6d6f2072022-09-12 23:08:34 -040056 Interest interest = makeControlCommandRequest(Name("/prefix/" + module + "/verb"),
57 {}, format, identity);
58 if (modifyInterest) {
Junxiao Shi8a1f1702017-07-03 00:05:08 +000059 modifyInterest(interest);
Junxiao Shid7631272016-08-17 04:16:31 +000060 }
61
Davide Pesavento20cafa82022-07-25 01:15:03 -040062 const auto& authorization = authorizations.at(module);
Junxiao Shid7631272016-08-17 04:16:31 +000063
64 bool isAccepted = false;
65 bool isRejected = false;
Junxiao Shi8a1f1702017-07-03 00:05:08 +000066 authorization(Name("/prefix"), interest, nullptr,
Junxiao Shid7631272016-08-17 04:16:31 +000067 [this, &isAccepted, &isRejected] (const std::string& requester) {
68 BOOST_REQUIRE_MESSAGE(!isAccepted && !isRejected,
69 "authorization function should invoke only one continuation");
70 isAccepted = true;
71 lastRequester = requester;
72 },
73 [this, &isAccepted, &isRejected] (ndn::mgmt::RejectReply act) {
74 BOOST_REQUIRE_MESSAGE(!isAccepted && !isRejected,
75 "authorization function should invoke only one continuation");
76 isRejected = true;
77 lastRejectReply = act;
78 });
79
Davide Pesaventod96744d2018-02-03 19:16:07 -050080 this->advanceClocks(1_ms, 10);
Junxiao Shid7631272016-08-17 04:16:31 +000081 BOOST_REQUIRE_MESSAGE(isAccepted || isRejected,
82 "authorization function should invoke one continuation");
83 return isAccepted;
84 }
85
86protected:
Davide Pesavento20cafa82022-07-25 01:15:03 -040087 shared_ptr<CommandAuthenticator> authenticator = CommandAuthenticator::create();
Junxiao Shid7631272016-08-17 04:16:31 +000088 std::unordered_map<std::string, ndn::mgmt::Authorization> authorizations;
89 std::string lastRequester;
90 ndn::mgmt::RejectReply lastRejectReply;
91};
92
93BOOST_AUTO_TEST_SUITE(Mgmt)
94BOOST_FIXTURE_TEST_SUITE(TestCommandAuthenticator, CommandAuthenticatorFixture)
95
96BOOST_AUTO_TEST_CASE(Certs)
97{
98 Name id0("/localhost/CommandAuthenticator/0");
99 Name id1("/localhost/CommandAuthenticator/1");
100 Name id2("/localhost/CommandAuthenticator/2");
Davide Pesavento21353752020-11-20 00:43:44 -0500101 BOOST_REQUIRE(m_keyChain.createIdentity(id0));
102 BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
103 BOOST_REQUIRE(saveIdentityCert(id2, "2.ndncert", true));
Junxiao Shid7631272016-08-17 04:16:31 +0000104
105 makeModules({"module0", "module1", "module2", "module3", "module4", "module5", "module6", "module7"});
Davide Pesavento20cafa82022-07-25 01:15:03 -0400106 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000107 authorizations
108 {
109 authorize
110 {
111 certfile any
112 privileges
113 {
114 module1
115 module3
116 module5
117 module7
118 }
119 }
120 authorize
121 {
122 certfile "1.ndncert"
123 privileges
124 {
125 module2
126 module3
127 module6
128 module7
129 }
130 }
131 authorize
132 {
133 certfile "2.ndncert"
134 privileges
135 {
136 module4
137 module5
138 module6
139 module7
140 }
141 }
142 }
143 )CONFIG";
144 loadConfig(config);
145
146 // module0: none
147 BOOST_CHECK_EQUAL(authorize("module0", id0), false);
148 BOOST_CHECK_EQUAL(authorize("module0", id1), false);
149 BOOST_CHECK_EQUAL(authorize("module0", id2), false);
150
151 // module1: any
152 BOOST_CHECK_EQUAL(authorize("module1", id0), true);
153 BOOST_CHECK_EQUAL(authorize("module1", id1), true);
154 BOOST_CHECK_EQUAL(authorize("module1", id2), true);
155
156 // module2: id1
157 BOOST_CHECK_EQUAL(authorize("module2", id0), false);
158 BOOST_CHECK_EQUAL(authorize("module2", id1), true);
159 BOOST_CHECK_EQUAL(authorize("module2", id2), false);
160
161 // module3: any,id1
162 BOOST_CHECK_EQUAL(authorize("module3", id0), true);
163 BOOST_CHECK_EQUAL(authorize("module3", id1), true);
164 BOOST_CHECK_EQUAL(authorize("module3", id2), true);
165
166 // module4: id2
167 BOOST_CHECK_EQUAL(authorize("module4", id0), false);
168 BOOST_CHECK_EQUAL(authorize("module4", id1), false);
169 BOOST_CHECK_EQUAL(authorize("module4", id2), true);
170
171 // module5: any,id2
172 BOOST_CHECK_EQUAL(authorize("module5", id0), true);
173 BOOST_CHECK_EQUAL(authorize("module5", id1), true);
174 BOOST_CHECK_EQUAL(authorize("module5", id2), true);
175
176 // module6: id1,id2
177 BOOST_CHECK_EQUAL(authorize("module6", id0), false);
178 BOOST_CHECK_EQUAL(authorize("module6", id1), true);
179 BOOST_CHECK_EQUAL(authorize("module6", id2), true);
180
181 // module7: any,id1,id2
182 BOOST_CHECK_EQUAL(authorize("module7", id0), true);
183 BOOST_CHECK_EQUAL(authorize("module7", id1), true);
184 BOOST_CHECK_EQUAL(authorize("module7", id2), true);
185}
186
187BOOST_AUTO_TEST_CASE(Requester)
188{
189 Name id0("/localhost/CommandAuthenticator/0");
190 Name id1("/localhost/CommandAuthenticator/1");
Davide Pesavento21353752020-11-20 00:43:44 -0500191 BOOST_REQUIRE(m_keyChain.createIdentity(id0));
192 BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
Junxiao Shid7631272016-08-17 04:16:31 +0000193
194 makeModules({"module0", "module1"});
Davide Pesavento20cafa82022-07-25 01:15:03 -0400195 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000196 authorizations
197 {
198 authorize
199 {
200 certfile any
201 privileges
202 {
203 module0
204 }
205 }
206 authorize
207 {
208 certfile "1.ndncert"
209 privileges
210 {
211 module1
212 }
213 }
214 }
215 )CONFIG";
216 loadConfig(config);
217
218 // module0: any
219 BOOST_CHECK_EQUAL(authorize("module0", id0), true);
220 BOOST_CHECK_EQUAL(lastRequester, "*");
221 BOOST_CHECK_EQUAL(authorize("module0", id1), true);
222 BOOST_CHECK_EQUAL(lastRequester, "*");
223
224 // module1: id1
225 BOOST_CHECK_EQUAL(authorize("module1", id0), false);
226 BOOST_CHECK_EQUAL(authorize("module1", id1), true);
227 BOOST_CHECK(id1.isPrefixOf(lastRequester));
228}
229
230class IdentityAuthorizedFixture : public CommandAuthenticatorFixture
231{
232protected:
233 IdentityAuthorizedFixture()
Junxiao Shid7631272016-08-17 04:16:31 +0000234 {
Davide Pesavento21353752020-11-20 00:43:44 -0500235 BOOST_REQUIRE(saveIdentityCert(id1, "1.ndncert", true));
Junxiao Shid7631272016-08-17 04:16:31 +0000236
237 makeModules({"module1"});
Davide Pesavento20cafa82022-07-25 01:15:03 -0400238 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000239 authorizations
240 {
241 authorize
242 {
243 certfile "1.ndncert"
244 privileges
245 {
246 module1
247 }
248 }
249 }
250 )CONFIG";
251 loadConfig(config);
252 }
253
254 bool
Davide Pesavento87fc0f82018-04-11 23:43:51 -0400255 authorize1(const std::function<void(Interest&)>& modifyInterest)
Junxiao Shid7631272016-08-17 04:16:31 +0000256 {
257 return authorize("module1", id1, modifyInterest);
258 }
259
260protected:
Davide Pesavento20cafa82022-07-25 01:15:03 -0400261 const Name id1{"/localhost/CommandAuthenticator/1"};
Junxiao Shid7631272016-08-17 04:16:31 +0000262};
263
Davide Pesavento20cafa82022-07-25 01:15:03 -0400264BOOST_FIXTURE_TEST_SUITE(Reject, IdentityAuthorizedFixture)
Junxiao Shid7631272016-08-17 04:16:31 +0000265
266BOOST_AUTO_TEST_CASE(BadKeyLocator_NameTooShort)
267{
268 BOOST_CHECK_EQUAL(authorize1(
269 [] (Interest& interest) {
270 interest.setName("/prefix");
271 }
272 ), false);
273 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::SILENT);
274}
275
Davide Pesavento20cafa82022-07-25 01:15:03 -0400276BOOST_AUTO_TEST_CASE(BadSigInfo)
Junxiao Shid7631272016-08-17 04:16:31 +0000277{
278 BOOST_CHECK_EQUAL(authorize1(
279 [] (Interest& interest) {
280 setNameComponent(interest, ndn::signed_interest::POS_SIG_INFO, "not-SignatureInfo");
281 }
282 ), false);
283 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::SILENT);
284}
285
Davide Pesavento20cafa82022-07-25 01:15:03 -0400286BOOST_AUTO_TEST_CASE(MissingKeyLocator)
Junxiao Shid7631272016-08-17 04:16:31 +0000287{
288 BOOST_CHECK_EQUAL(authorize1(
289 [] (Interest& interest) {
Junxiao Shia9079802017-08-26 14:12:30 +0000290 ndn::SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
Davide Pesaventodeb54272022-03-11 18:51:05 -0500291 setNameComponent(interest, ndn::signed_interest::POS_SIG_INFO, ndn::make_span(sigInfo.wireEncode()));
Junxiao Shid7631272016-08-17 04:16:31 +0000292 }
293 ), false);
294 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::SILENT);
295}
296
Davide Pesavento20cafa82022-07-25 01:15:03 -0400297BOOST_AUTO_TEST_CASE(BadKeyLocatorType)
Junxiao Shid7631272016-08-17 04:16:31 +0000298{
299 BOOST_CHECK_EQUAL(authorize1(
300 [] (Interest& interest) {
301 ndn::KeyLocator kl;
Davide Pesaventoa599d2a2022-02-16 18:52:43 -0500302 kl.setKeyDigest(ndn::makeBinaryBlock(tlv::KeyDigest,
303 {0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD}));
Junxiao Shia9079802017-08-26 14:12:30 +0000304 ndn::SignatureInfo sigInfo(tlv::SignatureSha256WithRsa);
Junxiao Shid7631272016-08-17 04:16:31 +0000305 sigInfo.setKeyLocator(kl);
Davide Pesaventodeb54272022-03-11 18:51:05 -0500306 setNameComponent(interest, ndn::signed_interest::POS_SIG_INFO, ndn::make_span(sigInfo.wireEncode()));
Junxiao Shid7631272016-08-17 04:16:31 +0000307 }
308 ), false);
309 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::SILENT);
310}
311
Junxiao Shid7631272016-08-17 04:16:31 +0000312BOOST_AUTO_TEST_CASE(NotAuthorized)
313{
314 Name id0("/localhost/CommandAuthenticator/0");
Davide Pesavento21353752020-11-20 00:43:44 -0500315 BOOST_REQUIRE(m_keyChain.createIdentity(id0));
Junxiao Shid7631272016-08-17 04:16:31 +0000316
317 BOOST_CHECK_EQUAL(authorize("module1", id0), false);
318 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::STATUS403);
319}
320
321BOOST_AUTO_TEST_CASE(BadSig)
322{
323 BOOST_CHECK_EQUAL(authorize1(
324 [] (Interest& interest) {
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000325 setNameComponent(interest, ndn::command_interest::POS_SIG_VALUE, "bad-signature-bits");
Junxiao Shid7631272016-08-17 04:16:31 +0000326 }
327 ), false);
328 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::STATUS403);
329}
330
331BOOST_AUTO_TEST_CASE(InvalidTimestamp)
332{
333 name::Component timestampComp;
334 BOOST_CHECK_EQUAL(authorize1(
335 [&timestampComp] (const Interest& interest) {
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000336 timestampComp = interest.getName().at(ndn::command_interest::POS_TIMESTAMP);
Junxiao Shid7631272016-08-17 04:16:31 +0000337 }
338 ), true); // accept first command
339 BOOST_CHECK_EQUAL(authorize1(
340 [&timestampComp] (Interest& interest) {
Alexander Afanasyev635bf202017-03-09 21:57:34 +0000341 setNameComponent(interest, ndn::command_interest::POS_TIMESTAMP, timestampComp);
Junxiao Shid7631272016-08-17 04:16:31 +0000342 }
343 ), false); // reject second command because timestamp equals first command
344 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::STATUS403);
345}
346
Davide Pesaventod96744d2018-02-03 19:16:07 -0500347BOOST_FIXTURE_TEST_CASE(MissingAuthorizationsSection, CommandAuthenticatorFixture)
348{
349 Name id0("/localhost/CommandAuthenticator/0");
Davide Pesavento21353752020-11-20 00:43:44 -0500350 BOOST_REQUIRE(m_keyChain.createIdentity(id0));
Davide Pesaventod96744d2018-02-03 19:16:07 -0500351
352 makeModules({"module42"});
353 loadConfig("");
354
355 BOOST_CHECK_EQUAL(authorize("module42", id0), false);
356 BOOST_CHECK(lastRejectReply == ndn::mgmt::RejectReply::STATUS403);
357}
358
Davide Pesavento20cafa82022-07-25 01:15:03 -0400359BOOST_AUTO_TEST_SUITE_END() // Reject
Junxiao Shid7631272016-08-17 04:16:31 +0000360
361BOOST_AUTO_TEST_SUITE(BadConfig)
362
363BOOST_AUTO_TEST_CASE(EmptyAuthorizationsSection)
364{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400365 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000366 authorizations
367 {
368 }
369 )CONFIG";
370
371 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
372}
373
374BOOST_AUTO_TEST_CASE(UnrecognizedKey)
375{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400376 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000377 authorizations
378 {
379 unrecognized_key
380 {
381 }
382 }
383 )CONFIG";
384
385 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
386}
387
388BOOST_AUTO_TEST_CASE(CertfileMissing)
389{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400390 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000391 authorizations
392 {
393 authorize
394 {
395 privileges
396 {
397 }
398 }
399 }
400 )CONFIG";
401
402 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
403}
404
405BOOST_AUTO_TEST_CASE(CertUnreadable)
406{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400407 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000408 authorizations
409 {
410 authorize
411 {
412 certfile "1.ndncert"
413 privileges
414 {
415 }
416 }
417 }
418 )CONFIG";
419
420 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
421}
422
423BOOST_AUTO_TEST_CASE(PrivilegesMissing)
424{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400425 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000426 authorizations
427 {
428 authorize
429 {
430 certfile any
431 }
432 }
433 )CONFIG";
434
435 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
436}
437
438BOOST_AUTO_TEST_CASE(UnregisteredModule)
439{
Davide Pesavento20cafa82022-07-25 01:15:03 -0400440 const std::string config = R"CONFIG(
Junxiao Shid7631272016-08-17 04:16:31 +0000441 authorizations
442 {
443 authorize
444 {
445 certfile any
446 privileges
447 {
448 nosuchmodule
449 }
450 }
451 }
452 )CONFIG";
453
454 BOOST_CHECK_THROW(loadConfig(config), ConfigFile::Error);
455}
456
457BOOST_AUTO_TEST_SUITE_END() // BadConfig
458
459BOOST_AUTO_TEST_SUITE_END() // TestCommandAuthenticator
460BOOST_AUTO_TEST_SUITE_END() // Mgmt
461
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400462} // namespace nfd::tests