Message ID | 20220516152038.39594-8-konstantin.meskhidze@huawei.com (mailing list archive) |
---|---|
State | Handled Elsewhere |
Headers | show |
Series | Network support for Landlock | expand |
On 16/05/2022 17:20, Konstantin Meskhidze wrote: > This modification adds network rules support > in internal landlock functions (presented in ruleset.c) > and landlock_create_ruleset syscall. > > Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> > --- > > 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. > + 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. > + > 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 >
5/17/2022 11:27 AM, Mickaël Salaün пишет: > > > On 16/05/2022 17:20, Konstantin Meskhidze wrote: >> This modification adds network rules support >> in internal landlock functions (presented in ruleset.c) >> and landlock_create_ruleset syscall. >> >> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> >> --- >> >> 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. > > >> + 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. > >> + >> 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 >> > .
On 19/05/2022 11:27, Konstantin Meskhidze wrote: > > > 5/17/2022 11:27 AM, Mickaël Salaün пишет: [...] >>> >>> @@ -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. To be clear, it is kind of a partial revert of patch 5/15.
5/19/2022 5:42 PM, Mickaël Salaün пишет: > > On 19/05/2022 11:27, Konstantin Meskhidze wrote: >> >> >> 5/17/2022 11:27 AM, Mickaël Salaün пишет: > > [...] > > >>>> >>>> @@ -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. > > To be clear, it is kind of a partial revert of patch 5/15. Yep. Thank you for noticing that. > .
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. */ + 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); + 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; }
This modification adds network rules support in internal landlock functions (presented in ruleset.c) and landlock_create_ruleset syscall. Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com> --- 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(-) -- 2.25.1