Thread (56 messages) 56 messages, 2 authors, 2022-05-25

Re: [PATCH v5 07/15] landlock: add support network rules

From: Konstantin Meskhidze <hidden>
Date: 2022-05-19 09:27:27
Also in: netdev, netfilter-devel


5/17/2022 11:27 AM, Mickaël Salaün пишет:

On 16/05/2022 17:20, Konstantin Meskhidze wrote:
quoted
This modification adds network rules support
in internal landlock functions (presented in ruleset.c)
and landlock_create_ruleset syscall.

Signed-off-by: Konstantin Meskhidze <redacted>
---

Changes since v3:
* Split commit.
* Add network rule support for internal landlock functions.
* Add set_mask and get_mask for network.
* Add rb_root root_net_port.

Changes since v4:
* Refactoring landlock_create_ruleset() - splits ruleset and
masks checks.
* Refactoring landlock_create_ruleset() and landlock mask
setters/getters to support two rule types.
* Refactoring landlock_add_rule syscall add_rule_path_beneath
function by factoring out get_ruleset_from_fd() and
landlock_put_ruleset().

---
  security/landlock/limits.h   |  8 +++-
  security/landlock/ruleset.c  | 82 +++++++++++++++++++++++++++++++-----
  security/landlock/ruleset.h  | 34 +++++++++++++--
  security/landlock/syscalls.c | 45 +++++++++++---------
  4 files changed, 132 insertions(+), 37 deletions(-)
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index b54184ab9439..23694bf05cb7 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -22,6 +22,12 @@
  #define LANDLOCK_MASK_ACCESS_FS        ((LANDLOCK_LAST_ACCESS_FS << 
1) - 1)
  #define LANDLOCK_NUM_ACCESS_FS        
__const_hweight64(LANDLOCK_MASK_ACCESS_FS)

-/* clang-format on */
+#define LANDLOCK_LAST_ACCESS_NET    LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET    ((LANDLOCK_LAST_ACCESS_NET << 1) 
- 1)
+#define LANDLOCK_NUM_ACCESS_NET        
__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
+#define LANDLOCK_MASK_SHIFT_NET        16
+
+#define LANDLOCK_RULE_TYPE_NUM        LANDLOCK_RULE_NET_SERVICE

+/* clang-format on */
  #endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index c4ed783d655b..ea9ecb3f471a 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const 
u32 num_layers)
      refcount_set(&new_ruleset->usage, 1);
      mutex_init(&new_ruleset->lock);
      new_ruleset->root_inode = RB_ROOT;
+    new_ruleset->root_net_port = RB_ROOT;
      new_ruleset->num_layers = num_layers;
      /*
       * hierarchy = NULL
@@ -46,17 +47,21 @@ static struct landlock_ruleset 
*create_ruleset(const u32 num_layers)
  }

  struct landlock_ruleset *landlock_create_ruleset(
-        const access_mask_t access_mask)
+                    const access_mask_t access_mask_fs,
+                    const access_mask_t access_mask_net)
  {
      struct landlock_ruleset *new_ruleset;

      /* Informs about useless ruleset. */
-    if (!access_mask)
+    if (!access_mask_fs && !access_mask_net)
          return ERR_PTR(-ENOMSG);
      new_ruleset = create_ruleset(1);
-    if (!IS_ERR(new_ruleset))
-        landlock_set_fs_access_mask(new_ruleset, access_mask, 0);
-
+    if (IS_ERR(new_ruleset))
+        return new_ruleset;
+    if (access_mask_fs)
+        landlock_set_fs_access_mask(new_ruleset, access_mask_fs, 0);
+    if (access_mask_net)
+        landlock_set_net_access_mask(new_ruleset, access_mask_net, 0);
      return new_ruleset;
  }
@@ -94,9 +99,11 @@ static struct landlock_rule *create_rule(
          return ERR_PTR(-ENOMEM);
      RB_CLEAR_NODE(&new_rule->node);

-    if (object_ptr) {
+    if (object_ptr && !object_data) {
          landlock_get_object(object_ptr);
          new_rule->object.ptr = object_ptr;
+    } else if (object_data && !object_ptr) {
+        new_rule->object.data = object_data;
      } else if (object_ptr && object_data) {
          WARN_ON_ONCE(1);
          return ERR_PTR(-EINVAL);
@@ -132,10 +139,12 @@ static void build_check_ruleset(void)
          .num_layers = ~0,
      };
      typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
+    typeof(ruleset.access_masks[0]) net_access_mask = ~0;

      BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
      BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
      BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS);
+    BUILD_BUG_ON(net_access_mask < LANDLOCK_MASK_ACCESS_NET);
  }

  /**
@@ -183,6 +192,11 @@ static int insert_rule(struct landlock_ruleset 
*const ruleset,
          object_data = (uintptr_t)object_ptr;
          root = &ruleset->root_inode;
          break;
+    case LANDLOCK_RULE_NET_SERVICE:
+        if (WARN_ON_ONCE(object_ptr))
+            return -EINVAL;
+        root = &ruleset->root_net_port;
+        break;
      default:
          WARN_ON_ONCE(1);
          return -EINVAL;
@@ -237,6 +251,16 @@ static int insert_rule(struct landlock_ruleset 
*const ruleset,
                      &ruleset->root_inode);
              free_rule(this, rule_type);
              break;
+        case LANDLOCK_RULE_NET_SERVICE:
+            new_rule = create_rule(NULL, object_data,
+                           &this->layers, this->num_layers,
+                           &(*layers)[0]);
+            if (IS_ERR(new_rule))
+                return PTR_ERR(new_rule);
+            rb_replace_node(&this->node, &new_rule->node,
+                    &ruleset->root_net_port);
+            free_rule(this, rule_type);
+            break;
          }
          return 0;
      }
@@ -254,6 +278,15 @@ static int insert_rule(struct landlock_ruleset 
*const ruleset,
          rb_link_node(&new_rule->node, parent_node, walker_node);
          rb_insert_color(&new_rule->node, &ruleset->root_inode);
          break;
+    case LANDLOCK_RULE_NET_SERVICE:
+        new_rule = create_rule(NULL, object_data, layers,
+                       num_layers, NULL);
+        if (IS_ERR(new_rule))
+            return PTR_ERR(new_rule);
+        rb_link_node(&new_rule->node, parent_node, walker_node);
+        rb_insert_color(&new_rule->node, &ruleset->root_net_port);
+        ruleset->num_rules++;
+        break;
      }
      return 0;
  }
@@ -315,6 +348,9 @@ static int tree_merge(struct landlock_ruleset 
*const src,
      case LANDLOCK_RULE_PATH_BENEATH:
          src_root = &src->root_inode;
          break;
+    case LANDLOCK_RULE_NET_SERVICE:
+        src_root = &src->root_net_port;
+        break;
      default:
          return -EINVAL;
      }
@@ -341,6 +377,11 @@ static int tree_merge(struct landlock_ruleset 
*const src,
                        rule_type, &layers,
                        ARRAY_SIZE(layers));
              break;
+        case LANDLOCK_RULE_NET_SERVICE:
+            err = insert_rule(dst, NULL, walker_rule->object.data,
+                      rule_type, &layers,
+                      ARRAY_SIZE(layers));
+            break;
          }
          if (err)
              return err;
@@ -376,6 +417,10 @@ static int merge_ruleset(struct landlock_ruleset 
*const dst,
      err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
      if (err)
          goto out_unlock;
+    /* Merges the @src network tree. */
+    err = tree_merge(src, dst, LANDLOCK_RULE_NET_SERVICE);
+    if (err)
+        goto out_unlock;

  out_unlock:
      mutex_unlock(&src->lock);
@@ -395,6 +440,9 @@ static int tree_copy(struct landlock_ruleset 
*const parent,
      case LANDLOCK_RULE_PATH_BENEATH:
          parent_root = &parent->root_inode;
          break;
+    case LANDLOCK_RULE_NET_SERVICE:
+        parent_root = &parent->root_net_port;
+        break;
      default:
          return -EINVAL;
      }
@@ -407,6 +455,12 @@ static int tree_copy(struct landlock_ruleset 
*const parent,
                        rule_type, &walker_rule->layers,
                        walker_rule->num_layers);
              break;
+        case LANDLOCK_RULE_NET_SERVICE:
+            err = insert_rule(child, NULL,
+                      walker_rule->object.data, rule_type,
+                      &walker_rule->layers,
+                      walker_rule->num_layers);
+            break;
          }
          if (err)
              return err;
@@ -429,6 +483,10 @@ static int inherit_ruleset(struct 
landlock_ruleset *const parent,

      /* Copies the @parent inode tree. */
      err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
+    if (err)
+        goto out_unlock;
+    /* Copies the @parent inode tree. */
Not the inode tree this time.
   Right. Thanks for noticing.
quoted
+    err = tree_copy(parent, child, LANDLOCK_RULE_NET_SERVICE);
      if (err)
          goto out_unlock;
@@ -463,9 +521,11 @@ static void free_ruleset(struct landlock_ruleset 
*const ruleset)

      might_sleep();
      rbtree_postorder_for_each_entry_safe(freeme, next,
-                         &ruleset->root_inode,
-                         node)
+                         &ruleset->root_inode, node)
          free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
+    rbtree_postorder_for_each_entry_safe(freeme, next,
+                         &ruleset->root_net_port, node)
+        free_rule(freeme, LANDLOCK_RULE_NET_SERVICE);
      put_hierarchy(ruleset->hierarchy);
      kfree(ruleset);
  }
@@ -560,13 +620,13 @@ const struct landlock_rule *landlock_find_rule(
  {
      const struct rb_node *node;

-    if (!object_data)
-        return NULL;
-
      switch (rule_type) {
      case LANDLOCK_RULE_PATH_BENEATH:
          node = ruleset->root_inode.rb_node;
          break;
+    case LANDLOCK_RULE_NET_SERVICE:
+        node = ruleset->root_net_port.rb_node;
+        break;
      default:
          WARN_ON_ONCE(1);
          return NULL;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index f3cd890d0348..916b30b31c06 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -102,6 +102,12 @@ struct landlock_ruleset {
       * tree is immutable until @usage reaches zero.
       */
      struct rb_root root_inode;
+    /**
+     * @root_net_port: Root of a red-black tree containing object nodes
+     * for network port. Once a ruleset is tied to a process (i.e. as 
a domain),
+     * this tree is immutable until @usage reaches zero.
+     */
+    struct rb_root root_net_port;
      /**
       * @hierarchy: Enables hierarchy identification even when a parent
       * domain vanishes.  This is needed for the ptrace protection.
@@ -157,7 +163,8 @@ struct landlock_ruleset {
  };

  struct landlock_ruleset *landlock_create_ruleset(
-        const access_mask_t access_mask);
+                    const access_mask_t access_mask_fs,
+                    const access_mask_t access_mask_net);

  void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
  void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
ruleset);
@@ -183,11 +190,12 @@ static inline void landlock_get_ruleset(struct 
landlock_ruleset *const ruleset)
  }

  /* A helper function to set a filesystem mask */
-static inline void landlock_set_fs_access_mask(struct 
landlock_ruleset *ruleset,
-                 const access_mask_t access_maskset,
+static inline void landlock_set_fs_access_mask(
+                 struct landlock_ruleset *ruleset,
+                 const access_mask_t access_mask_fs,
                   u16 mask_level)
  {
-    ruleset->access_masks[mask_level] = access_maskset;
+    ruleset->access_masks[mask_level] = access_mask_fs;
  }

  /* A helper function to get a filesystem mask */
@@ -198,6 +206,24 @@ static inline u32 landlock_get_fs_access_mask(
      return (ruleset->access_masks[mask_level] & 
LANDLOCK_MASK_ACCESS_FS);
  }

+/* A helper function to set a network mask */
+static inline void landlock_set_net_access_mask(
+                  struct landlock_ruleset *ruleset,
+                  const access_mask_t access_mask_net,
+                  u16 mask_level)
+{
+    ruleset->access_masks[mask_level] |= (access_mask_net <<
+                          LANDLOCK_MASK_SHIFT_NET);
+}
+
+/* A helper function to get a network mask */
+static inline u32 landlock_get_net_access_mask(
+                const struct landlock_ruleset *ruleset,
+                u16 mask_level)
+{
+    return (ruleset->access_masks[mask_level] >> 
LANDLOCK_MASK_SHIFT_NET);
+}
+
  access_mask_t get_handled_accesses(
          const struct landlock_ruleset *const domain,
          u16 rule_type, u16 num_access);
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 31f9facec123..812541f4e155 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
          LANDLOCK_MASK_ACCESS_FS)
          return -EINVAL;

+    /* Checks network content (and 32-bits cast). */
+    if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
+            LANDLOCK_MASK_ACCESS_NET)
+        return -EINVAL;
+
      /* Checks arguments and transforms to kernel struct. */
-    ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
+    ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
+                      ruleset_attr.handled_access_net);
      if (IS_ERR(ruleset))
          return PTR_ERR(ruleset);
@@ -275,21 +281,17 @@ static int get_path_from_fd(const s32 fd, struct 
path *const path)
      return err;
  }

-static int add_rule_path_beneath(const int ruleset_fd, const void 
*const rule_attr)
+static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
+                 const void *const rule_attr)
  {
      struct landlock_path_beneath_attr path_beneath_attr;
      struct path path;
-    struct landlock_ruleset *ruleset;
      int res, err;
-
-    /* Gets and checks the ruleset. */
-    ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
-    if (IS_ERR(ruleset))
-        return PTR_ERR(ruleset);
+    u32 mask;

      /* Copies raw user space buffer, only one type for now. */
      res = copy_from_user(&path_beneath_attr, rule_attr,
-                sizeof(path_beneath_attr));
+            sizeof(path_beneath_attr));
      if (res)
          return -EFAULT;
@@ -298,32 +300,26 @@ static int add_rule_path_beneath(const int 
ruleset_fd, const void *const rule_at
       * are ignored in path walks.
       */
      if (!path_beneath_attr.allowed_access) {
-        err = -ENOMSG;
-        goto out_put_ruleset;
+        return -ENOMSG;
      }
      /*
       * Checks that allowed_access matches the @ruleset constraints
       * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
       */
-    if ((path_beneath_attr.allowed_access |
-        landlock_get_fs_access_mask(ruleset, 0)) !=
-                landlock_get_fs_access_mask(ruleset, 0)) {
-        err = -EINVAL;
-        goto out_put_ruleset;
-    }
+    mask = landlock_get_fs_access_mask(ruleset, 0);
+    if ((path_beneath_attr.allowed_access | mask) != mask)
+        return -EINVAL;

      /* Gets and checks the new rule. */
      err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
      if (err)
-        goto out_put_ruleset;
+        return err;

      /* Imports the new rule. */
      err = landlock_append_fs_rule(ruleset, &path,
                        path_beneath_attr.allowed_access);
      path_put(&path);

-out_put_ruleset:
-    landlock_put_ruleset(ruleset);
      return err;
  }
@@ -360,6 +356,7 @@ SYSCALL_DEFINE4(landlock_add_rule,
          const int, ruleset_fd, const enum landlock_rule_type, 
rule_type,
          const void __user *const, rule_attr, const __u32, flags)
  {
+    struct landlock_ruleset *ruleset;
      int err;

      if (!landlock_initialized)
@@ -369,14 +366,20 @@ SYSCALL_DEFINE4(landlock_add_rule,
      if (flags)
          return -EINVAL;

+    /* Gets and checks the ruleset. */
+    ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
+    if (IS_ERR(ruleset))
+        return PTR_ERR(ruleset);
This shouldn't be part of this patch.
   I agree. I will move it into another patch.
quoted
+
      switch (rule_type) {
      case LANDLOCK_RULE_PATH_BENEATH:
-        err = add_rule_path_beneath(ruleset_fd, rule_attr);
+        err = add_rule_path_beneath(ruleset, rule_attr);
          break;
      default:
          err = -EINVAL;
          break;
      }
+    landlock_put_ruleset(ruleset);
      return err;
  }

-- 
2.25.1
.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help