77 * Copyright © 2021-2022 Microsoft Corporation
88 */
99
10+ #include <kunit/test.h>
1011#include <linux/atomic.h>
1112#include <linux/bitops.h>
1213#include <linux/bits.h>
@@ -311,6 +312,119 @@ static bool no_more_access(
311312 return true;
312313}
313314
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+
314428/*
315429 * Removes @layer_masks accesses that are not requested.
316430 *
@@ -331,6 +445,57 @@ scope_to_request(const access_mask_t access_request,
331445 return !memchr_inv (layer_masks , 0 , sizeof (* layer_masks ));
332446}
333447
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+
334499/*
335500 * Returns true if there is at least one access right different than
336501 * LANDLOCK_ACCESS_FS_REFER.
@@ -354,6 +519,51 @@ is_eacces(const layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS],
354519 return false;
355520}
356521
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+
357567/**
358568 * is_access_to_paths_allowed - Check accesses for requests with a common path
359569 *
@@ -1225,3 +1435,27 @@ __init void landlock_add_fs_hooks(void)
12251435 security_add_hooks (landlock_hooks , ARRAY_SIZE (landlock_hooks ),
12261436 & landlock_lsmid );
12271437}
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