7
7
* Copyright © 2021-2022 Microsoft Corporation
8
8
*/
9
9
10
+ #include <kunit/test.h>
10
11
#include <linux/atomic.h>
11
12
#include <linux/bitops.h>
12
13
#include <linux/bits.h>
@@ -311,6 +312,119 @@ static bool no_more_access(
311
312
return true;
312
313
}
313
314
315
+ #define NMA_TRUE (...) KUNIT_EXPECT_TRUE(test, no_more_access(__VA_ARGS__))
316
+ #define NMA_FALSE (...) KUNIT_EXPECT_FALSE(test, no_more_access(__VA_ARGS__))
317
+
318
+ #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
319
+
320
+ static void test_no_more_access (struct kunit * const test )
321
+ {
322
+ const layer_mask_t rx0 [LANDLOCK_NUM_ACCESS_FS ] = {
323
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ),
324
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_READ_FILE )] = BIT_ULL (0 ),
325
+ };
326
+ const layer_mask_t mx0 [LANDLOCK_NUM_ACCESS_FS ] = {
327
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ),
328
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_MAKE_REG )] = BIT_ULL (0 ),
329
+ };
330
+ const layer_mask_t x0 [LANDLOCK_NUM_ACCESS_FS ] = {
331
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ),
332
+ };
333
+ const layer_mask_t x1 [LANDLOCK_NUM_ACCESS_FS ] = {
334
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (1 ),
335
+ };
336
+ const layer_mask_t x01 [LANDLOCK_NUM_ACCESS_FS ] = {
337
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ) |
338
+ BIT_ULL (1 ),
339
+ };
340
+ const layer_mask_t allows_all [LANDLOCK_NUM_ACCESS_FS ] = {};
341
+
342
+ /* Checks without restriction. */
343
+ NMA_TRUE (& x0 , & allows_all , false, & allows_all , NULL , false);
344
+ NMA_TRUE (& allows_all , & x0 , false, & allows_all , NULL , false);
345
+ NMA_FALSE (& x0 , & x0 , false, & allows_all , NULL , false);
346
+
347
+ /*
348
+ * Checks that we can only refer a file if no more access could be
349
+ * inherited.
350
+ */
351
+ NMA_TRUE (& x0 , & x0 , false, & rx0 , NULL , false);
352
+ NMA_TRUE (& rx0 , & rx0 , false, & rx0 , NULL , false);
353
+ NMA_FALSE (& rx0 , & rx0 , false, & x0 , NULL , false);
354
+ NMA_FALSE (& rx0 , & rx0 , false, & x1 , NULL , false);
355
+
356
+ /* Checks allowed referring with different nested domains. */
357
+ NMA_TRUE (& x0 , & x1 , false, & x0 , NULL , false);
358
+ NMA_TRUE (& x1 , & x0 , false, & x0 , NULL , false);
359
+ NMA_TRUE (& x0 , & x01 , false, & x0 , NULL , false);
360
+ NMA_TRUE (& x0 , & x01 , false, & rx0 , NULL , false);
361
+ NMA_TRUE (& x01 , & x0 , false, & x0 , NULL , false);
362
+ NMA_TRUE (& x01 , & x0 , false, & rx0 , NULL , false);
363
+ NMA_FALSE (& x01 , & x01 , false, & x0 , NULL , false);
364
+
365
+ /* Checks that file access rights are also enforced for a directory. */
366
+ NMA_FALSE (& rx0 , & rx0 , true, & x0 , NULL , false);
367
+
368
+ /* Checks that directory access rights don't impact file referring... */
369
+ NMA_TRUE (& mx0 , & mx0 , false, & x0 , NULL , false);
370
+ /* ...but only directory referring. */
371
+ NMA_FALSE (& mx0 , & mx0 , true, & x0 , NULL , false);
372
+
373
+ /* Checks directory exchange. */
374
+ NMA_TRUE (& mx0 , & mx0 , true, & mx0 , & mx0 , true);
375
+ NMA_TRUE (& mx0 , & mx0 , true, & mx0 , & x0 , true);
376
+ NMA_FALSE (& mx0 , & mx0 , true, & x0 , & mx0 , true);
377
+ NMA_FALSE (& mx0 , & mx0 , true, & x0 , & x0 , true);
378
+ NMA_FALSE (& mx0 , & mx0 , true, & x1 , & x1 , true);
379
+
380
+ /* Checks file exchange with directory access rights... */
381
+ NMA_TRUE (& mx0 , & mx0 , false, & mx0 , & mx0 , false);
382
+ NMA_TRUE (& mx0 , & mx0 , false, & mx0 , & x0 , false);
383
+ NMA_TRUE (& mx0 , & mx0 , false, & x0 , & mx0 , false);
384
+ NMA_TRUE (& mx0 , & mx0 , false, & x0 , & x0 , false);
385
+ /* ...and with file access rights. */
386
+ NMA_TRUE (& rx0 , & rx0 , false, & rx0 , & rx0 , false);
387
+ NMA_TRUE (& rx0 , & rx0 , false, & rx0 , & x0 , false);
388
+ NMA_FALSE (& rx0 , & rx0 , false, & x0 , & rx0 , false);
389
+ NMA_FALSE (& rx0 , & rx0 , false, & x0 , & x0 , false);
390
+ NMA_FALSE (& rx0 , & rx0 , false, & x1 , & x1 , false);
391
+
392
+ /*
393
+ * Allowing the following requests should not be a security risk
394
+ * because domain 0 denies execute access, and domain 1 is always
395
+ * nested with domain 0. However, adding an exception for this case
396
+ * would mean to check all nested domains to make sure none can get
397
+ * more privileges (e.g. processes only sandboxed by domain 0).
398
+ * Moreover, this behavior (i.e. composition of N domains) could then
399
+ * be inconsistent compared to domain 1's ruleset alone (e.g. it might
400
+ * be denied to link/rename with domain 1's ruleset, whereas it would
401
+ * be allowed if nested on top of domain 0). Another drawback would be
402
+ * to create a cover channel that could enable sandboxed processes to
403
+ * infer most of the filesystem restrictions from their domain. To
404
+ * make it simple, efficient, safe, and more consistent, this case is
405
+ * always denied.
406
+ */
407
+ NMA_FALSE (& x1 , & x1 , false, & x0 , NULL , false);
408
+ NMA_FALSE (& x1 , & x1 , false, & rx0 , NULL , false);
409
+ NMA_FALSE (& x1 , & x1 , true, & x0 , NULL , false);
410
+ NMA_FALSE (& x1 , & x1 , true, & rx0 , NULL , false);
411
+
412
+ /* Checks the same case of exclusive domains with a file... */
413
+ NMA_TRUE (& x1 , & x1 , false, & x01 , NULL , false);
414
+ NMA_FALSE (& x1 , & x1 , false, & x01 , & x0 , false);
415
+ NMA_FALSE (& x1 , & x1 , false, & x01 , & x01 , false);
416
+ NMA_FALSE (& x1 , & x1 , false, & x0 , & x0 , false);
417
+ /* ...and with a directory. */
418
+ NMA_FALSE (& x1 , & x1 , false, & x0 , & x0 , true);
419
+ NMA_FALSE (& x1 , & x1 , true, & x0 , & x0 , false);
420
+ NMA_FALSE (& x1 , & x1 , true, & x0 , & x0 , true);
421
+ }
422
+
423
+ #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
424
+
425
+ #undef NMA_TRUE
426
+ #undef NMA_FALSE
427
+
314
428
/*
315
429
* Removes @layer_masks accesses that are not requested.
316
430
*
@@ -331,6 +445,57 @@ scope_to_request(const access_mask_t access_request,
331
445
return !memchr_inv (layer_masks , 0 , sizeof (* layer_masks ));
332
446
}
333
447
448
+ #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
449
+
450
+ static void test_scope_to_request_with_exec_none (struct kunit * const test )
451
+ {
452
+ /* Allows everything. */
453
+ layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {};
454
+
455
+ /* Checks and scopes with execute. */
456
+ KUNIT_EXPECT_TRUE (test , scope_to_request (LANDLOCK_ACCESS_FS_EXECUTE ,
457
+ & layer_masks ));
458
+ KUNIT_EXPECT_EQ (test , 0 ,
459
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )]);
460
+ KUNIT_EXPECT_EQ (test , 0 ,
461
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )]);
462
+ }
463
+
464
+ static void test_scope_to_request_with_exec_some (struct kunit * const test )
465
+ {
466
+ /* Denies execute and write. */
467
+ layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {
468
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ),
469
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )] = BIT_ULL (1 ),
470
+ };
471
+
472
+ /* Checks and scopes with execute. */
473
+ KUNIT_EXPECT_FALSE (test , scope_to_request (LANDLOCK_ACCESS_FS_EXECUTE ,
474
+ & layer_masks ));
475
+ KUNIT_EXPECT_EQ (test , BIT_ULL (0 ),
476
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )]);
477
+ KUNIT_EXPECT_EQ (test , 0 ,
478
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )]);
479
+ }
480
+
481
+ static void test_scope_to_request_without_access (struct kunit * const test )
482
+ {
483
+ /* Denies execute and write. */
484
+ layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {
485
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )] = BIT_ULL (0 ),
486
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )] = BIT_ULL (1 ),
487
+ };
488
+
489
+ /* Checks and scopes without access request. */
490
+ KUNIT_EXPECT_TRUE (test , scope_to_request (0 , & layer_masks ));
491
+ KUNIT_EXPECT_EQ (test , 0 ,
492
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_EXECUTE )]);
493
+ KUNIT_EXPECT_EQ (test , 0 ,
494
+ layer_masks [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )]);
495
+ }
496
+
497
+ #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
498
+
334
499
/*
335
500
* Returns true if there is at least one access right different than
336
501
* LANDLOCK_ACCESS_FS_REFER.
@@ -354,6 +519,51 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
354
519
return false;
355
520
}
356
521
522
+ #define IE_TRUE (...) KUNIT_EXPECT_TRUE(test, is_eacces(__VA_ARGS__))
523
+ #define IE_FALSE (...) KUNIT_EXPECT_FALSE(test, is_eacces(__VA_ARGS__))
524
+
525
+ #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
526
+
527
+ static void test_is_eacces_with_none (struct kunit * const test )
528
+ {
529
+ const layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {};
530
+
531
+ IE_FALSE (& layer_masks , 0 );
532
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_REFER );
533
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_EXECUTE );
534
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_WRITE_FILE );
535
+ }
536
+
537
+ static void test_is_eacces_with_refer (struct kunit * const test )
538
+ {
539
+ const layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {
540
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_REFER )] = BIT_ULL (0 ),
541
+ };
542
+
543
+ IE_FALSE (& layer_masks , 0 );
544
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_REFER );
545
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_EXECUTE );
546
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_WRITE_FILE );
547
+ }
548
+
549
+ static void test_is_eacces_with_write (struct kunit * const test )
550
+ {
551
+ const layer_mask_t layer_masks [LANDLOCK_NUM_ACCESS_FS ] = {
552
+ [BIT_INDEX (LANDLOCK_ACCESS_FS_WRITE_FILE )] = BIT_ULL (0 ),
553
+ };
554
+
555
+ IE_FALSE (& layer_masks , 0 );
556
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_REFER );
557
+ IE_FALSE (& layer_masks , LANDLOCK_ACCESS_FS_EXECUTE );
558
+
559
+ IE_TRUE (& layer_masks , LANDLOCK_ACCESS_FS_WRITE_FILE );
560
+ }
561
+
562
+ #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
563
+
564
+ #undef IE_TRUE
565
+ #undef IE_FALSE
566
+
357
567
/**
358
568
* is_access_to_paths_allowed - Check accesses for requests with a common path
359
569
*
@@ -1225,3 +1435,27 @@ __init void landlock_add_fs_hooks(void)
1225
1435
security_add_hooks (landlock_hooks , ARRAY_SIZE (landlock_hooks ),
1226
1436
& landlock_lsmid );
1227
1437
}
1438
+
1439
+ #ifdef CONFIG_SECURITY_LANDLOCK_KUNIT_TEST
1440
+
1441
+ /* clang-format off */
1442
+ static struct kunit_case test_cases [] = {
1443
+ KUNIT_CASE (test_no_more_access ),
1444
+ KUNIT_CASE (test_scope_to_request_with_exec_none ),
1445
+ KUNIT_CASE (test_scope_to_request_with_exec_some ),
1446
+ KUNIT_CASE (test_scope_to_request_without_access ),
1447
+ KUNIT_CASE (test_is_eacces_with_none ),
1448
+ KUNIT_CASE (test_is_eacces_with_refer ),
1449
+ KUNIT_CASE (test_is_eacces_with_write ),
1450
+ {}
1451
+ };
1452
+ /* clang-format on */
1453
+
1454
+ static struct kunit_suite test_suite = {
1455
+ .name = "landlock_fs" ,
1456
+ .test_cases = test_cases ,
1457
+ };
1458
+
1459
+ kunit_test_suite (test_suite );
1460
+
1461
+ #endif /* CONFIG_SECURITY_LANDLOCK_KUNIT_TEST */
0 commit comments