From 4baf6e33251b37f111e21289f8ee71fe4cce236e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 1 Apr 2012 12:09:55 -0700 Subject: cgroup: convert all non-memcg controllers to the new cftype interface Convert debug, freezer, cpuset, cpu_cgroup, cpuacct, net_prio, blkio, net_cls and device controllers to use the new cftype based interface. Termination entry is added to cftype arrays and populate callbacks are replaced with cgroup_subsys->base_cftypes initializations. This is functionally identical transformation. There shouldn't be any visible behavior change. memcg is rather special and will be converted separately. Signed-off-by: Tejun Heo Acked-by: Li Zefan Cc: Paul Menage Cc: Ingo Molnar Cc: Peter Zijlstra Cc: "David S. Miller" Cc: Vivek Goyal --- security/device_cgroup.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/device_cgroup.c b/security/device_cgroup.c index c43a3323feea..442204cc22d9 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -447,22 +447,16 @@ static struct cftype dev_cgroup_files[] = { .read_seq_string = devcgroup_seq_read, .private = DEVCG_LIST, }, + { } /* terminate */ }; -static int devcgroup_populate(struct cgroup_subsys *ss, - struct cgroup *cgroup) -{ - return cgroup_add_files(cgroup, ss, dev_cgroup_files, - ARRAY_SIZE(dev_cgroup_files)); -} - struct cgroup_subsys devices_subsys = { .name = "devices", .can_attach = devcgroup_can_attach, .create = devcgroup_create, .destroy = devcgroup_destroy, - .populate = devcgroup_populate, .subsys_id = devices_subsys_id, + .base_cftypes = dev_cgroup_files, }; int __devcgroup_inode_permission(struct inode *inode, int mask) -- cgit v1.2.2 From c4a4d603796c727b9555867571f89483be9c565e Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 23:15:31 -0800 Subject: userns: Use cred->user_ns instead of cred->user->user_ns Optimize performance and prepare for the removal of the user_ns reference from user_struct. Remove the slow long walk through cred->user->user_ns and instead go straight to cred->user_ns. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 14 +++++++------- security/keys/key.c | 2 +- security/keys/permission.c | 2 +- security/keys/process_keys.c | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index 0cf4b53480a7..8b3e10e2eac7 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -81,7 +81,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, return 0; /* Do we have the necessary capabilities? */ - if (targ_ns == cred->user->user_ns) + if (targ_ns == cred->user_ns) return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM; /* Have we tried all of the parent namespaces? */ @@ -136,10 +136,10 @@ int cap_ptrace_access_check(struct task_struct *child, unsigned int mode) rcu_read_lock(); cred = current_cred(); child_cred = __task_cred(child); - if (cred->user->user_ns == child_cred->user->user_ns && + if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) goto out; - if (ns_capable(child_cred->user->user_ns, CAP_SYS_PTRACE)) + if (ns_capable(child_cred->user_ns, CAP_SYS_PTRACE)) goto out; ret = -EPERM; out: @@ -168,10 +168,10 @@ int cap_ptrace_traceme(struct task_struct *parent) rcu_read_lock(); cred = __task_cred(parent); child_cred = current_cred(); - if (cred->user->user_ns == child_cred->user->user_ns && + if (cred->user_ns == child_cred->user_ns && cap_issubset(child_cred->cap_permitted, cred->cap_permitted)) goto out; - if (has_ns_capability(parent, child_cred->user->user_ns, CAP_SYS_PTRACE)) + if (has_ns_capability(parent, child_cred->user_ns, CAP_SYS_PTRACE)) goto out; ret = -EPERM; out: @@ -214,7 +214,7 @@ static inline int cap_inh_is_capped(void) /* they are so limited unless the current task has the CAP_SETPCAP * capability */ - if (cap_capable(current_cred(), current_cred()->user->user_ns, + if (cap_capable(current_cred(), current_cred()->user_ns, CAP_SETPCAP, SECURITY_CAP_AUDIT) == 0) return 0; return 1; @@ -866,7 +866,7 @@ int cap_task_prctl(int option, unsigned long arg2, unsigned long arg3, || ((new->securebits & SECURE_ALL_LOCKS & ~arg2)) /*[2]*/ || (arg2 & ~(SECURE_ALL_LOCKS | SECURE_ALL_BITS)) /*[3]*/ || (cap_capable(current_cred(), - current_cred()->user->user_ns, CAP_SETPCAP, + current_cred()->user_ns, CAP_SETPCAP, SECURITY_CAP_AUDIT) != 0) /*[4]*/ /* * [1] no changing of bits that are locked diff --git a/security/keys/key.c b/security/keys/key.c index 06783cffb3af..7e6034793af3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -253,7 +253,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, quotalen = desclen + type->def_datalen; /* get hold of the key tracking for this user */ - user = key_user_lookup(uid, cred->user->user_ns); + user = key_user_lookup(uid, cred->user_ns); if (!user) goto no_memory_1; diff --git a/security/keys/permission.c b/security/keys/permission.c index c35b5229e3cd..e146cbd714bd 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -36,7 +36,7 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, key = key_ref_to_ptr(key_ref); - if (key->user->user_ns != cred->user->user_ns) + if (key->user->user_ns != cred->user_ns) goto use_other_perms; /* use the second 8-bits of permissions for keys the caller owns */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index be7ecb2018dd..70febff06da9 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -858,7 +858,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); - new->user_ns = new->user->user_ns; + new->user_ns = new->user_ns; new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; -- cgit v1.2.2 From 0093ccb68f3753c0ba4d74c89d7e0f444b8d6123 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:52:53 -0800 Subject: cred: Refcount the user_ns pointed to by the cred. struct user_struct will shortly loose it's user_ns reference so make the cred user_ns reference a proper reference complete with reference counting. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/keys/process_keys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 70febff06da9..447fb7618ff3 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -858,7 +858,7 @@ void key_replace_session_keyring(void) new-> sgid = old-> sgid; new->fsgid = old->fsgid; new->user = get_uid(old->user); - new->user_ns = new->user_ns; + new->user_ns = get_user_ns(new->user_ns); new->group_info = get_group_info(old->group_info); new->securebits = old->securebits; -- cgit v1.2.2 From aeb3ae9da9b50a386b22af786d19b623e8d9f0fa Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 16 Nov 2011 21:59:43 -0800 Subject: userns: Add an explicit reference to the parent user namespace I am about to remove the struct user_namespace reference from struct user_struct. So keep an explicit track of the parent user namespace. Take advantage of this new reference and replace instances of user_ns->creator->user_ns with user_ns->parent. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index 8b3e10e2eac7..435d074853f3 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -92,7 +92,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, *If you have a capability in a parent user ns, then you have * it over all children user namespaces as well. */ - targ_ns = targ_ns->creator->user_ns; + targ_ns = targ_ns->parent; } /* We never get here */ -- cgit v1.2.2 From 47a93a5bcb131879d4425d4559e90ad82990825d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 16 Feb 2012 15:08:39 -0500 Subject: SELinux: allow seek operations on the file exposing policy sesearch uses: lseek(3, 0, SEEK_SET) = -1 ESPIPE (Illegal seek) Make that work. Signed-off-by: Eric Paris --- security/selinux/selinuxfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index d7018bfa1f00..d6ae2d407307 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -496,6 +496,7 @@ static const struct file_operations sel_policy_ops = { .read = sel_read_policy, .mmap = sel_mmap_policy, .release = sel_release_policy, + .llseek = generic_file_llseek, }; static ssize_t sel_write_load(struct file *file, const char __user *buf, -- cgit v1.2.2 From 72e8c8593f8fdb983d9cd79d824f6b48ef21f14f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 16 Feb 2012 15:08:39 -0500 Subject: SELinux: loosen DAC perms on reading policy There is no reason the DAC perms on reading the policy file need to be root only. There are selinux checks which should control this access. Signed-off-by: Eric Paris --- security/selinux/selinuxfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index d6ae2d407307..f4b5a0baaec4 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1832,7 +1832,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO}, [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO}, - [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUSR}, + [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO}, /* last one */ {""} }; ret = simple_fill_super(sb, SELINUX_MAGIC, selinux_files); -- cgit v1.2.2 From aa893269de6277b44be88e25dcd5331c934c29c4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Mar 2012 14:35:12 -0400 Subject: SELinux: allow default source/target selectors for user/role/range When new objects are created we have great and flexible rules to determine the type of the new object. We aren't quite as flexible or mature when it comes to determining the user, role, and range. This patch adds a new ability to specify the place a new objects user, role, and range should come from. For users and roles it can come from either the source or the target of the operation. aka for files the user can either come from the source (the running process and todays default) or it can come from the target (aka the parent directory of the new file) examples always are done with directory context: system_u:object_r:mnt_t:s0-s0:c0.c512 process context: unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 [no rule] unconfined_u:object_r:mnt_t:s0 test_none [default user source] unconfined_u:object_r:mnt_t:s0 test_user_source [default user target] system_u:object_r:mnt_t:s0 test_user_target [default role source] unconfined_u:unconfined_r:mnt_t:s0 test_role_source [default role target] unconfined_u:object_r:mnt_t:s0 test_role_target [default range source low] unconfined_u:object_r:mnt_t:s0 test_range_source_low [default range source high] unconfined_u:object_r:mnt_t:s0:c0.c1023 test_range_source_high [default range source low-high] unconfined_u:object_r:mnt_t:s0-s0:c0.c1023 test_range_source_low-high [default range target low] unconfined_u:object_r:mnt_t:s0 test_range_target_low [default range target high] unconfined_u:object_r:mnt_t:s0:c0.c512 test_range_target_high [default range target low-high] unconfined_u:object_r:mnt_t:s0-s0:c0.c512 test_range_target_low-high Signed-off-by: Eric Paris --- security/selinux/include/security.h | 3 ++- security/selinux/ss/context.h | 20 ++++++++++++++++++++ security/selinux/ss/mls.c | 24 ++++++++++++++++++++++++ security/selinux/ss/policydb.c | 25 +++++++++++++++++++++++++ security/selinux/ss/policydb.h | 13 +++++++++++++ security/selinux/ss/services.c | 32 +++++++++++++++++++++++++------- 6 files changed, 109 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index d871e8ad2103..ba53400195c0 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -31,13 +31,14 @@ #define POLICYDB_VERSION_BOUNDARY 24 #define POLICYDB_VERSION_FILENAME_TRANS 25 #define POLICYDB_VERSION_ROLETRANS 26 +#define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_ROLETRANS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEW_OBJECT_DEFAULTS #endif /* Mask for just the mount related flags */ diff --git a/security/selinux/ss/context.h b/security/selinux/ss/context.h index 45e8fb0515f8..212e3479a0d9 100644 --- a/security/selinux/ss/context.h +++ b/security/selinux/ss/context.h @@ -74,6 +74,26 @@ out: return rc; } +/* + * Sets both levels in the MLS range of 'dst' to the high level of 'src'. + */ +static inline int mls_context_cpy_high(struct context *dst, struct context *src) +{ + int rc; + + dst->range.level[0].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[0].cat, &src->range.level[1].cat); + if (rc) + goto out; + + dst->range.level[1].sens = src->range.level[1].sens; + rc = ebitmap_cpy(&dst->range.level[1].cat, &src->range.level[1].cat); + if (rc) + ebitmap_destroy(&dst->range.level[0].cat); +out: + return rc; +} + static inline int mls_context_cmp(struct context *c1, struct context *c2) { return ((c1->range.level[0].sens == c2->range.level[0].sens) && diff --git a/security/selinux/ss/mls.c b/security/selinux/ss/mls.c index fbf9c5816c71..40de8d3f208e 100644 --- a/security/selinux/ss/mls.c +++ b/security/selinux/ss/mls.c @@ -517,6 +517,8 @@ int mls_compute_sid(struct context *scontext, { struct range_trans rtr; struct mls_range *r; + struct class_datum *cladatum; + int default_range = 0; if (!policydb.mls_enabled) return 0; @@ -530,6 +532,28 @@ int mls_compute_sid(struct context *scontext, r = hashtab_search(policydb.range_tr, &rtr); if (r) return mls_range_set(newcontext, r); + + if (tclass && tclass <= policydb.p_classes.nprim) { + cladatum = policydb.class_val_to_struct[tclass - 1]; + if (cladatum) + default_range = cladatum->default_range; + } + + switch (default_range) { + case DEFAULT_SOURCE_LOW: + return mls_context_cpy_low(newcontext, scontext); + case DEFAULT_SOURCE_HIGH: + return mls_context_cpy_high(newcontext, scontext); + case DEFAULT_SOURCE_LOW_HIGH: + return mls_context_cpy(newcontext, scontext); + case DEFAULT_TARGET_LOW: + return mls_context_cpy_low(newcontext, tcontext); + case DEFAULT_TARGET_HIGH: + return mls_context_cpy_high(newcontext, tcontext); + case DEFAULT_TARGET_LOW_HIGH: + return mls_context_cpy(newcontext, tcontext); + } + /* Fallthrough */ case AVTAB_CHANGE: if ((tclass == policydb.process_class) || (sock == true)) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a7f61d52f05c..2bb9c2fd5f1a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -133,6 +133,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_NEW_OBJECT_DEFAULTS, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -1306,6 +1311,16 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; } + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + rc = next_entry(buf, fp, sizeof(u32) * 3); + if (rc) + goto bad; + + cladatum->default_user = le32_to_cpu(buf[0]); + cladatum->default_role = le32_to_cpu(buf[1]); + cladatum->default_range = le32_to_cpu(buf[2]); + } + rc = hashtab_insert(h, key, cladatum); if (rc) goto bad; @@ -2832,6 +2847,16 @@ static int class_write(void *vkey, void *datum, void *ptr) if (rc) return rc; + if (p->policyvers >= POLICYDB_VERSION_NEW_OBJECT_DEFAULTS) { + buf[0] = cpu_to_le32(cladatum->default_user); + buf[1] = cpu_to_le32(cladatum->default_role); + buf[2] = cpu_to_le32(cladatum->default_range); + + rc = put_entry(buf, sizeof(uint32_t), 3, fp); + if (rc) + return rc; + } + return 0; } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index b846c0387180..a949f1ad43bb 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -60,6 +60,19 @@ struct class_datum { struct symtab permissions; /* class-specific permission symbol table */ struct constraint_node *constraints; /* constraints on class permissions */ struct constraint_node *validatetrans; /* special transition rules */ + /* Options how a new object user and role should be decided */ +#define DEFAULT_SOURCE 1 +#define DEFAULT_TARGET 2 + char default_user; + char default_role; +/* Options how a new object range should be decided */ +#define DEFAULT_SOURCE_LOW 1 +#define DEFAULT_SOURCE_HIGH 2 +#define DEFAULT_SOURCE_LOW_HIGH 3 +#define DEFAULT_TARGET_LOW 4 +#define DEFAULT_TARGET_HIGH 5 +#define DEFAULT_TARGET_LOW_HIGH 6 + char default_range; }; /* Role attributes */ diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 185f849a26f6..2ea108c2c048 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1389,6 +1389,7 @@ static int security_compute_sid(u32 ssid, u32 *out_sid, bool kern) { + struct class_datum *cladatum = NULL; struct context *scontext = NULL, *tcontext = NULL, newcontext; struct role_trans *roletr = NULL; struct avtab_key avkey; @@ -1437,12 +1438,20 @@ static int security_compute_sid(u32 ssid, goto out_unlock; } + if (tclass && tclass <= policydb.p_classes.nprim) + cladatum = policydb.class_val_to_struct[tclass - 1]; + /* Set the user identity. */ switch (specified) { case AVTAB_TRANSITION: case AVTAB_CHANGE: - /* Use the process user identity. */ - newcontext.user = scontext->user; + if (cladatum && cladatum->default_user == DEFAULT_TARGET) { + newcontext.user = tcontext->user; + } else { + /* notice this gets both DEFAULT_SOURCE and unset */ + /* Use the process user identity. */ + newcontext.user = scontext->user; + } break; case AVTAB_MEMBER: /* Use the related object owner. */ @@ -1450,14 +1459,23 @@ static int security_compute_sid(u32 ssid, break; } - /* Set the role and type to default values. */ - if ((tclass == policydb.process_class) || (sock == true)) { - /* Use the current role and type of process. */ + /* Set the role to default values. */ + if (cladatum && cladatum->default_role == DEFAULT_SOURCE) { newcontext.role = scontext->role; + } else if (cladatum && cladatum->default_role == DEFAULT_TARGET) { + newcontext.role = tcontext->role; + } else { + if ((tclass == policydb.process_class) || (sock == true)) + newcontext.role = scontext->role; + else + newcontext.role = OBJECT_R_VAL; + } + + /* Set the type to default values. */ + if ((tclass == policydb.process_class) || (sock == true)) { + /* Use the type of process. */ newcontext.type = scontext->type; } else { - /* Use the well-defined object role. */ - newcontext.role = OBJECT_R_VAL; /* Use the type of the related object. */ newcontext.type = tcontext->type; } -- cgit v1.2.2 From eed7795d0a2c9b2e934afc088e903fa2c17b7958 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 20 Mar 2012 14:35:12 -0400 Subject: SELinux: add default_type statements Because Fedora shipped userspace based on my development tree we now have policy version 27 in the wild defining only default user, role, and range. Thus to add default_type we need a policy.28. Signed-off-by: Eric Paris --- security/selinux/include/security.h | 3 ++- security/selinux/ss/policydb.c | 19 +++++++++++++++++++ security/selinux/ss/policydb.h | 3 ++- security/selinux/ss/services.c | 14 ++++++++++---- 4 files changed, 33 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index ba53400195c0..dde2005407aa 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -32,13 +32,14 @@ #define POLICYDB_VERSION_FILENAME_TRANS 25 #define POLICYDB_VERSION_ROLETRANS 26 #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 +#define POLICYDB_VERSION_DEFAULT_TYPE 28 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_NEW_OBJECT_DEFAULTS +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_DEFAULT_TYPE #endif /* Mask for just the mount related flags */ diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 2bb9c2fd5f1a..9cd9b7c661ec 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -138,6 +138,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_DEFAULT_TYPE, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -1321,6 +1326,13 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) cladatum->default_range = le32_to_cpu(buf[2]); } + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + rc = next_entry(buf, fp, sizeof(u32) * 1); + if (rc) + goto bad; + cladatum->default_type = le32_to_cpu(buf[0]); + } + rc = hashtab_insert(h, key, cladatum); if (rc) goto bad; @@ -2857,6 +2869,13 @@ static int class_write(void *vkey, void *datum, void *ptr) return rc; } + if (p->policyvers >= POLICYDB_VERSION_DEFAULT_TYPE) { + buf[0] = cpu_to_le32(cladatum->default_type); + rc = put_entry(buf, sizeof(uint32_t), 1, fp); + if (rc) + return rc; + } + return 0; } diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index a949f1ad43bb..da637471d4ce 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -60,11 +60,12 @@ struct class_datum { struct symtab permissions; /* class-specific permission symbol table */ struct constraint_node *constraints; /* constraints on class permissions */ struct constraint_node *validatetrans; /* special transition rules */ - /* Options how a new object user and role should be decided */ +/* Options how a new object user, role, and type should be decided */ #define DEFAULT_SOURCE 1 #define DEFAULT_TARGET 2 char default_user; char default_role; + char default_type; /* Options how a new object range should be decided */ #define DEFAULT_SOURCE_LOW 1 #define DEFAULT_SOURCE_HIGH 2 diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 2ea108c2c048..1ded0ec7e8c2 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1472,12 +1472,18 @@ static int security_compute_sid(u32 ssid, } /* Set the type to default values. */ - if ((tclass == policydb.process_class) || (sock == true)) { - /* Use the type of process. */ + if (cladatum && cladatum->default_type == DEFAULT_SOURCE) { newcontext.type = scontext->type; - } else { - /* Use the type of the related object. */ + } else if (cladatum && cladatum->default_type == DEFAULT_TARGET) { newcontext.type = tcontext->type; + } else { + if ((tclass == policydb.process_class) || (sock == true)) { + /* Use the type of process. */ + newcontext.type = scontext->type; + } else { + /* Use the type of the related object. */ + newcontext.type = tcontext->type; + } } /* Look for a type transition/member/change rule. */ -- cgit v1.2.2 From 95dbf739313f09c8d859bde1373bc264ef979337 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:45:34 -0400 Subject: SELinux: check OPEN on truncate calls In RH BZ 578841 we realized that the SELinux sandbox program was allowed to truncate files outside of the sandbox. The reason is because sandbox confinement is determined almost entirely by the 'open' permission. The idea was that if the sandbox was unable to open() files it would be unable to do harm to those files. This turns out to be false in light of syscalls like truncate() and chmod() which don't require a previous open() call. I looked at the syscalls that did not have an associated 'open' check and found that truncate(), did not have a seperate permission and even if it did have a separate permission such a permission owuld be inadequate for use by sandbox (since it owuld have to be granted so liberally as to be useless). This patch checks the OPEN permission on truncate. I think a better solution for sandbox is a whole new permission, but at least this fixes what we have today. Signed-off-by: Eric Paris --- security/selinux/hooks.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d85b793c9321..f7d7e779c7f3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2708,6 +2708,7 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) { const struct cred *cred = current_cred(); unsigned int ia_valid = iattr->ia_valid; + __u32 av = FILE__WRITE; /* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */ if (ia_valid & ATTR_FORCE) { @@ -2721,7 +2722,10 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - return dentry_has_perm(cred, dentry, FILE__WRITE); + if (ia_valid & ATTR_SIZE) + av |= FILE__OPEN; + + return dentry_has_perm(cred, dentry, av); } static int selinux_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) -- cgit v1.2.2 From 83d498569e9a7a4b92c4c5d3566f2d6a604f28c9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:45:40 -0400 Subject: SELinux: rename dentry_open to file_open dentry_open takes a file, rename it to file_open Signed-off-by: Eric Paris --- security/apparmor/lsm.c | 4 ++-- security/capability.c | 4 ++-- security/security.c | 4 ++-- security/selinux/hooks.c | 6 +++--- security/smack/smack_lsm.c | 6 +++--- security/tomoyo/tomoyo.c | 6 +++--- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index ad05d391974d..02fddcd4c647 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -373,7 +373,7 @@ static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) AA_MAY_META_READ); } -static int apparmor_dentry_open(struct file *file, const struct cred *cred) +static int apparmor_file_open(struct file *file, const struct cred *cred) { struct aa_file_cxt *fcxt = file->f_security; struct aa_profile *profile; @@ -640,9 +640,9 @@ static struct security_operations apparmor_ops = { .path_chmod = apparmor_path_chmod, .path_chown = apparmor_path_chown, .path_truncate = apparmor_path_truncate, - .dentry_open = apparmor_dentry_open, .inode_getattr = apparmor_inode_getattr, + .file_open = apparmor_file_open, .file_permission = apparmor_file_permission, .file_alloc_security = apparmor_file_alloc_security, .file_free_security = apparmor_file_free_security, diff --git a/security/capability.c b/security/capability.c index 5bb21b1c448c..fca889676c5e 100644 --- a/security/capability.c +++ b/security/capability.c @@ -348,7 +348,7 @@ static int cap_file_receive(struct file *file) return 0; } -static int cap_dentry_open(struct file *file, const struct cred *cred) +static int cap_file_open(struct file *file, const struct cred *cred) { return 0; } @@ -956,7 +956,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, file_set_fowner); set_to_cap_if_null(ops, file_send_sigiotask); set_to_cap_if_null(ops, file_receive); - set_to_cap_if_null(ops, dentry_open); + set_to_cap_if_null(ops, file_open); set_to_cap_if_null(ops, task_create); set_to_cap_if_null(ops, task_free); set_to_cap_if_null(ops, cred_alloc_blank); diff --git a/security/security.c b/security/security.c index bf619ffc9a4d..5497a57fba01 100644 --- a/security/security.c +++ b/security/security.c @@ -701,11 +701,11 @@ int security_file_receive(struct file *file) return security_ops->file_receive(file); } -int security_dentry_open(struct file *file, const struct cred *cred) +int security_file_open(struct file *file, const struct cred *cred) { int ret; - ret = security_ops->dentry_open(file, cred); + ret = security_ops->file_open(file, cred); if (ret) return ret; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index f7d7e779c7f3..dc15f16a357c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2973,7 +2973,7 @@ static int selinux_file_permission(struct file *file, int mask) if (sid == fsec->sid && fsec->isid == isec->sid && fsec->pseqno == avc_policy_seqno()) - /* No change since dentry_open check. */ + /* No change since file_open check. */ return 0; return selinux_revalidate_file_permission(file, mask); @@ -3232,7 +3232,7 @@ static int selinux_file_receive(struct file *file) return file_has_perm(cred, file, file_to_av(file)); } -static int selinux_dentry_open(struct file *file, const struct cred *cred) +static int selinux_file_open(struct file *file, const struct cred *cred) { struct file_security_struct *fsec; struct inode *inode; @@ -5596,7 +5596,7 @@ static struct security_operations selinux_ops = { .file_send_sigiotask = selinux_file_send_sigiotask, .file_receive = selinux_file_receive, - .dentry_open = selinux_dentry_open, + .file_open = selinux_file_open, .task_create = selinux_task_create, .cred_alloc_blank = selinux_cred_alloc_blank, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 81c03a597112..8ef0199ebca1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1349,7 +1349,7 @@ static int smack_file_receive(struct file *file) } /** - * smack_dentry_open - Smack dentry open processing + * smack_file_open - Smack dentry open processing * @file: the object * @cred: unused * @@ -1357,7 +1357,7 @@ static int smack_file_receive(struct file *file) * * Returns 0 */ -static int smack_dentry_open(struct file *file, const struct cred *cred) +static int smack_file_open(struct file *file, const struct cred *cred) { struct inode_smack *isp = file->f_path.dentry->d_inode->i_security; @@ -3538,7 +3538,7 @@ struct security_operations smack_ops = { .file_send_sigiotask = smack_file_send_sigiotask, .file_receive = smack_file_receive, - .dentry_open = smack_dentry_open, + .file_open = smack_file_open, .cred_alloc_blank = smack_cred_alloc_blank, .cred_free = smack_cred_free, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 620d37c159a3..c2d04a50f76a 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -319,14 +319,14 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, } /** - * tomoyo_dentry_open - Target for security_dentry_open(). + * tomoyo_file_open - Target for security_file_open(). * * @f: Pointer to "struct file". * @cred: Pointer to "struct cred". * * Returns 0 on success, negative value otherwise. */ -static int tomoyo_dentry_open(struct file *f, const struct cred *cred) +static int tomoyo_file_open(struct file *f, const struct cred *cred) { int flags = f->f_flags; /* Don't check read permission here if called from do_execve(). */ @@ -510,7 +510,7 @@ static struct security_operations tomoyo_security_ops = { .bprm_set_creds = tomoyo_bprm_set_creds, .bprm_check_security = tomoyo_bprm_check_security, .file_fcntl = tomoyo_file_fcntl, - .dentry_open = tomoyo_dentry_open, + .file_open = tomoyo_file_open, .path_truncate = tomoyo_path_truncate, .path_unlink = tomoyo_path_unlink, .path_mkdir = tomoyo_path_mkdir, -- cgit v1.2.2 From d6ea83ec6864e9297fa8b00ec3dae183413a90e3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:45:49 -0400 Subject: SELinux: audit failed attempts to set invalid labels We know that some yum operation is causing CAP_MAC_ADMIN failures. This implies that an RPM is laying down (or attempting to lay down) a file with an invalid label. The problem is that we don't have any information to track down the cause. This patch will cause such a failure to report the failed label in an SELINUX_ERR audit message. This is similar to the SELINUX_ERR reports on invalid transitions and things like that. It should help run down problems on what is trying to set invalid labels in the future. Resulting records look something like: type=AVC msg=audit(1319659241.138:71): avc: denied { mac_admin } for pid=2594 comm="chcon" capability=33 scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tclass=capability2 type=SELINUX_ERR msg=audit(1319659241.138:71): op=setxattr invalid_context=unconfined_u:object_r:hello:s0 type=SYSCALL msg=audit(1319659241.138:71): arch=c000003e syscall=188 success=no exit=-22 a0=a2c0e0 a1=390341b79b a2=a2d620 a3=1f items=1 ppid=2519 pid=2594 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="chcon" exe="/usr/bin/chcon" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null) type=CWD msg=audit(1319659241.138:71): cwd="/root" type=PATH msg=audit(1319659241.138:71): item=0 name="test" inode=785879 dev=fc:03 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:admin_home_t:s0 Signed-off-by: Eric Paris --- security/selinux/hooks.c | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index dc15f16a357c..c3ee902306d8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2792,8 +2792,25 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, rc = security_context_to_sid(value, size, &newsid); if (rc == -EINVAL) { - if (!capable(CAP_MAC_ADMIN)) + if (!capable(CAP_MAC_ADMIN)) { + struct audit_buffer *ab; + size_t audit_size; + const char *str; + + /* We strip a nul only if it is at the end, otherwise the + * context contains a nul and we should audit that */ + str = value; + if (str[size - 1] == '\0') + audit_size = size - 1; + else + audit_size = size; + ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + audit_log_format(ab, "op=setxattr invalid_context="); + audit_log_n_untrustedstring(ab, value, audit_size); + audit_log_end(ab); + return rc; + } rc = security_context_to_sid_force(value, size, &newsid); } if (rc) @@ -5335,8 +5352,23 @@ static int selinux_setprocattr(struct task_struct *p, } error = security_context_to_sid(value, size, &sid); if (error == -EINVAL && !strcmp(name, "fscreate")) { - if (!capable(CAP_MAC_ADMIN)) + if (!capable(CAP_MAC_ADMIN)) { + struct audit_buffer *ab; + size_t audit_size; + + /* We strip a nul only if it is at the end, otherwise the + * context contains a nul and we should audit that */ + if (str[size - 1] == '\0') + audit_size = size - 1; + else + audit_size = size; + ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_SELINUX_ERR); + audit_log_format(ab, "op=fscreate invalid_context="); + audit_log_n_untrustedstring(ab, value, audit_size); + audit_log_end(ab); + return error; + } error = security_context_to_sid_force(value, size, &sid); } -- cgit v1.2.2 From bb7081ab93582fd2557160549854200a5fc7b42a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:46:36 -0400 Subject: SELinux: possible NULL deref in context_struct_to_string It's possible that the caller passed a NULL for scontext. However if this is a defered mapping we might still attempt to call *scontext=kstrdup(). This is bad. Instead just return the len. Signed-off-by: Eric Paris --- security/selinux/ss/services.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1ded0ec7e8c2..9b7e7ed54e7e 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1018,9 +1018,11 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 if (context->len) { *scontext_len = context->len; - *scontext = kstrdup(context->str, GFP_ATOMIC); - if (!(*scontext)) - return -ENOMEM; + if (scontext) { + *scontext = kstrdup(context->str, GFP_ATOMIC); + if (!(*scontext)) + return -ENOMEM; + } return 0; } -- cgit v1.2.2 From 92ae9e82d9a2c4b9b388d6a9e7a4b2ccb0b4452f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:46:46 -0400 Subject: SELinux: remove needless sel_div function I'm not really sure what the idea behind the sel_div function is, but it's useless. Since a and b are both unsigned, it's impossible for a % b < 0. That means that part of the function never does anything. Thus it's just a normal /. Just do that instead. I don't even understand what that operation was supposed to mean in the signed case however.... If it was signed: sel_div(-2, 4) == ((-2 / 4) - ((-2 % 4) < 0)) ((0) - ((-2) < 0)) ((0) - (1)) (-1) What actually happens: sel_div(-2, 4) == ((18446744073709551614 / 4) - ((18446744073709551614 % 4) < 0)) ((4611686018427387903) - ((2 < 0)) (4611686018427387903 - 0) ((unsigned int)4611686018427387903) (4294967295) Neither makes a whole ton of sense to me. So I'm getting rid of the function entirely. Signed-off-by: Eric Paris --- security/selinux/selinuxfs.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index f4b5a0baaec4..640feaa06c08 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1533,11 +1533,6 @@ static int sel_make_initcon_files(struct dentry *dir) return 0; } -static inline unsigned int sel_div(unsigned long a, unsigned long b) -{ - return a / b - (a % b < 0); -} - static inline unsigned long sel_class_to_ino(u16 class) { return (class * (SEL_VEC_MAX + 1)) | SEL_CLASS_INO_OFFSET; @@ -1545,7 +1540,7 @@ static inline unsigned long sel_class_to_ino(u16 class) static inline u16 sel_ino_to_class(unsigned long ino) { - return sel_div(ino & SEL_INO_MASK, SEL_VEC_MAX + 1); + return (ino & SEL_INO_MASK) / (SEL_VEC_MAX + 1); } static inline unsigned long sel_perm_to_ino(u16 class, u32 perm) -- cgit v1.2.2 From 154c50ca4eb9ae472f50b6a481213e21ead4457d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 13:47:11 -0400 Subject: SELinux: if sel_make_bools errors don't leave inconsistent state We reset the bool names and values array to NULL, but do not reset the number of entries in these arrays to 0. If we error out and then get back into this function we will walk these NULL pointers based on the belief that they are non-zero length. Signed-off-by: Eric Paris cc: stable@kernel.org --- security/selinux/selinuxfs.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 640feaa06c08..4e93f9ef970b 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1233,6 +1233,7 @@ static int sel_make_bools(void) kfree(bool_pending_names[i]); kfree(bool_pending_names); kfree(bool_pending_values); + bool_num = 0; bool_pending_names = NULL; bool_pending_values = NULL; -- cgit v1.2.2 From 2e33405785d3eaec303c54b4a10afdebf3729da7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: SELinux: delay initialization of audit data in selinux_inode_permission We pay a rather large overhead initializing the common_audit_data. Since we only need this information if we actually emit an audit message there is little need to set it up in the hot path. This patch splits the functionality of avc_has_perm() into avc_has_perm_noaudit(), avc_audit_required() and slow_avc_audit(). But we take care of setting up to audit between required() and the actual audit call. Thus saving measurable time in a hot path. Signed-off-by: Stephen Smalley Signed-off-by: Eric Paris --- security/selinux/avc.c | 63 +------------------------------- security/selinux/hooks.c | 30 ++++++++++++++-- security/selinux/include/avc.h | 82 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 105 insertions(+), 70 deletions(-) (limited to 'security') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 8ee42b2a5f19..1a04247e3a17 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -458,7 +458,7 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) } /* This is the slow part of avc audit with big stack footprint */ -static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, +noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, u32 requested, u32 audited, u32 denied, struct common_audit_data *a, unsigned flags) @@ -496,67 +496,6 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, return 0; } -/** - * avc_audit - Audit the granting or denial of permissions. - * @ssid: source security identifier - * @tsid: target security identifier - * @tclass: target security class - * @requested: requested permissions - * @avd: access vector decisions - * @result: result from avc_has_perm_noaudit - * @a: auxiliary audit data - * @flags: VFS walk flags - * - * Audit the granting or denial of permissions in accordance - * with the policy. This function is typically called by - * avc_has_perm() after a permission check, but can also be - * called directly by callers who use avc_has_perm_noaudit() - * in order to separate the permission check from the auditing. - * For example, this separation is useful when the permission check must - * be performed under a lock, to allow the lock to be released - * before calling the auditing code. - */ -inline int avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, int result, struct common_audit_data *a, - unsigned flags) -{ - u32 denied, audited; - denied = requested & ~avd->allowed; - if (unlikely(denied)) { - audited = denied & avd->auditdeny; - /* - * a->selinux_audit_data->auditdeny is TRICKY! Setting a bit in - * this field means that ANY denials should NOT be audited if - * the policy contains an explicit dontaudit rule for that - * permission. Take notice that this is unrelated to the - * actual permissions that were denied. As an example lets - * assume: - * - * denied == READ - * avd.auditdeny & ACCESS == 0 (not set means explicit rule) - * selinux_audit_data->auditdeny & ACCESS == 1 - * - * We will NOT audit the denial even though the denied - * permission was READ and the auditdeny checks were for - * ACCESS - */ - if (a && - a->selinux_audit_data->auditdeny && - !(a->selinux_audit_data->auditdeny & avd->auditdeny)) - audited = 0; - } else if (result) - audited = denied = requested; - else - audited = requested & avd->auditallow; - if (likely(!audited)) - return 0; - - return slow_avc_audit(ssid, tsid, tclass, - requested, audited, denied, - a, flags); -} - /** * avc_add_callback - Register a callback for security events. * @callback: callback function diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c3ee902306d8..c99027dc0b36 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2684,6 +2684,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) u32 perms; bool from_access; unsigned flags = mask & MAY_NOT_BLOCK; + struct inode_security_struct *isec; + u32 sid; + struct av_decision avd; + int rc, rc2; + u32 audited, denied; from_access = mask & MAY_ACCESS; mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); @@ -2692,6 +2697,23 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (!mask) return 0; + validate_creds(cred); + + if (unlikely(IS_PRIVATE(inode))) + return 0; + + perms = file_mask_to_av(inode->i_mode, mask); + + sid = cred_sid(cred); + isec = inode->i_security; + + rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); + audited = avc_audit_required(perms, &avd, rc, + from_access ? FILE__AUDIT_ACCESS : 0, + &denied); + if (likely(!audited)) + return rc; + COMMON_AUDIT_DATA_INIT(&ad, INODE); ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2699,9 +2721,11 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (from_access) ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS; - perms = file_mask_to_av(inode->i_mode, mask); - - return inode_has_perm(cred, inode, perms, &ad, flags); + rc2 = slow_avc_audit(sid, isec->sid, isec->sclass, perms, + audited, denied, &ad, flags); + if (rc2) + return rc2; + return rc; } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index 1931370233d7..e4e50bb218ee 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -77,11 +77,83 @@ struct selinux_audit_data { void __init avc_init(void); -int avc_audit(u32 ssid, u32 tsid, - u16 tclass, u32 requested, - struct av_decision *avd, - int result, - struct common_audit_data *a, unsigned flags); +static inline u32 avc_audit_required(u32 requested, + struct av_decision *avd, + int result, + u32 auditdeny, + u32 *deniedp) +{ + u32 denied, audited; + denied = requested & ~avd->allowed; + if (unlikely(denied)) { + audited = denied & avd->auditdeny; + /* + * auditdeny is TRICKY! Setting a bit in + * this field means that ANY denials should NOT be audited if + * the policy contains an explicit dontaudit rule for that + * permission. Take notice that this is unrelated to the + * actual permissions that were denied. As an example lets + * assume: + * + * denied == READ + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) + * auditdeny & ACCESS == 1 + * + * We will NOT audit the denial even though the denied + * permission was READ and the auditdeny checks were for + * ACCESS + */ + if (auditdeny && !(auditdeny & avd->auditdeny)) + audited = 0; + } else if (result) + audited = denied = requested; + else + audited = requested & avd->auditallow; + *deniedp = denied; + return audited; +} + +int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, + u32 requested, u32 audited, u32 denied, + struct common_audit_data *a, + unsigned flags); + +/** + * avc_audit - Audit the granting or denial of permissions. + * @ssid: source security identifier + * @tsid: target security identifier + * @tclass: target security class + * @requested: requested permissions + * @avd: access vector decisions + * @result: result from avc_has_perm_noaudit + * @a: auxiliary audit data + * @flags: VFS walk flags + * + * Audit the granting or denial of permissions in accordance + * with the policy. This function is typically called by + * avc_has_perm() after a permission check, but can also be + * called directly by callers who use avc_has_perm_noaudit() + * in order to separate the permission check from the auditing. + * For example, this separation is useful when the permission check must + * be performed under a lock, to allow the lock to be released + * before calling the auditing code. + */ +static inline int avc_audit(u32 ssid, u32 tsid, + u16 tclass, u32 requested, + struct av_decision *avd, + int result, + struct common_audit_data *a, unsigned flags) +{ + u32 audited, denied; + audited = avc_audit_required(requested, avd, result, + a ? a->selinux_audit_data->auditdeny : 0, + &denied); + if (likely(!audited)) + return 0; + return slow_avc_audit(ssid, tsid, tclass, + requested, audited, denied, + a, flags); +} #define AVC_STRICT 1 /* Ignore permissive mode. */ int avc_has_perm_noaudit(u32 ssid, u32 tsid, -- cgit v1.2.2 From 602a8dd6ea6abd463bc26310c4a1b44919f88e68 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: SELinux: remove inode_has_perm_noadp Both callers could better be using file_has_perm() to get better audit results. Signed-off-by: Eric Paris --- security/selinux/hooks.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c99027dc0b36..8417a6afaf30 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1488,20 +1488,6 @@ static int inode_has_perm(const struct cred *cred, return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags); } -static int inode_has_perm_noadp(const struct cred *cred, - struct inode *inode, - u32 perms, - unsigned flags) -{ - struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; - - COMMON_AUDIT_DATA_INIT(&ad, INODE); - ad.u.inode = inode; - ad.selinux_audit_data = &sad; - return inode_has_perm(cred, inode, perms, &ad, flags); -} - /* Same as inode_has_perm, but pass explicit audit data containing the dentry to help the auditing code to more easily generate the pathname if needed. */ @@ -2128,21 +2114,17 @@ static inline void flush_unauthorized_files(const struct cred *cred, spin_lock(&tty_files_lock); if (!list_empty(&tty->tty_files)) { struct tty_file_private *file_priv; - struct inode *inode; /* Revalidate access to controlling tty. - Use inode_has_perm on the tty inode directly rather + Use path_has_perm on the tty path directly rather than using file_has_perm, as this particular open file may belong to another process and we are only interested in the inode-based check here. */ file_priv = list_first_entry(&tty->tty_files, struct tty_file_private, list); file = file_priv->file; - inode = file->f_path.dentry->d_inode; - if (inode_has_perm_noadp(cred, inode, - FILE__READ | FILE__WRITE, 0)) { + if (path_has_perm(cred, &file->f_path, FILE__READ | FILE__WRITE)) drop_tty = 1; - } } spin_unlock(&tty_files_lock); tty_kref_put(tty); @@ -3276,12 +3258,10 @@ static int selinux_file_receive(struct file *file) static int selinux_file_open(struct file *file, const struct cred *cred) { struct file_security_struct *fsec; - struct inode *inode; struct inode_security_struct *isec; - inode = file->f_path.dentry->d_inode; fsec = file->f_security; - isec = inode->i_security; + isec = file->f_path.dentry->d_inode->i_security; /* * Save inode label and policy sequence number * at open-time so that selinux_file_permission @@ -3299,7 +3279,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ - return inode_has_perm_noadp(cred, inode, open_file_to_av(file), 0); + return path_has_perm(cred, &file->f_path, open_file_to_av(file)); } /* task security operations */ -- cgit v1.2.2 From d4cf970d0732628d514405c5a975024b9e205b0b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: SELinux: move common_audit_data to a noinline slow path function selinux_inode_has_perm is a hot path. Instead of declaring the common_audit_data on the stack move it to a noinline function only used in the rare case we need to send an audit message. Signed-off-by: Eric Paris --- security/selinux/hooks.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 8417a6afaf30..b3bd8e1d268a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2658,11 +2658,29 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na return dentry_has_perm(cred, dentry, FILE__READ); } -static int selinux_inode_permission(struct inode *inode, int mask) +static noinline int audit_inode_permission(struct inode *inode, + u32 perms, u32 audited, u32 denied, + unsigned flags) { - const struct cred *cred = current_cred(); struct common_audit_data ad; struct selinux_audit_data sad = {0,}; + struct inode_security_struct *isec = inode->i_security; + int rc; + + COMMON_AUDIT_DATA_INIT(&ad, INODE); + ad.selinux_audit_data = &sad; + ad.u.inode = inode; + + rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, + audited, denied, &ad, flags); + if (rc) + return rc; + return 0; +} + +static int selinux_inode_permission(struct inode *inode, int mask) +{ + const struct cred *cred = current_cred(); u32 perms; bool from_access; unsigned flags = mask & MAY_NOT_BLOCK; @@ -2696,15 +2714,7 @@ static int selinux_inode_permission(struct inode *inode, int mask) if (likely(!audited)) return rc; - COMMON_AUDIT_DATA_INIT(&ad, INODE); - ad.selinux_audit_data = &sad; - ad.u.inode = inode; - - if (from_access) - ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS; - - rc2 = slow_avc_audit(sid, isec->sid, isec->sclass, perms, - audited, denied, &ad, flags); + rc2 = audit_inode_permission(inode, perms, audited, denied, flags); if (rc2) return rc2; return rc; -- cgit v1.2.2 From bd5e50f9c1c71daac273fa586424f07205f6b13b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: LSM: remove the COMMON_AUDIT_DATA_INIT type expansion Just open code it so grep on the source code works better. Signed-off-by: Eric Paris --- security/apparmor/capability.c | 2 +- security/apparmor/file.c | 2 +- security/apparmor/ipc.c | 2 +- security/apparmor/lib.c | 2 +- security/apparmor/lsm.c | 2 +- security/apparmor/policy.c | 2 +- security/apparmor/policy_unpack.c | 2 +- security/apparmor/resource.c | 2 +- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 68 +++++++++++++++++++-------------------- 10 files changed, 43 insertions(+), 43 deletions(-) (limited to 'security') diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 088dba3bf7dc..3ecb8b7d8502 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -65,7 +65,7 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, CAP); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_CAP); sa.aad = &aad; sa.tsk = task; sa.u.cap = cap; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 2f8fcba9ce4b..6ab264ca85cc 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -108,7 +108,7 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = op, aad.fs.request = request; diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index c3da93a5150d..dba449b74db3 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -42,7 +42,7 @@ static int aa_audit_ptrace(struct aa_profile *profile, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_PTRACE; aad.target = target; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index e75829ba0ff9..b11a2652f541 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -66,7 +66,7 @@ void aa_info_message(const char *str) if (audit_enabled) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 02fddcd4c647..4f7bc07b2dce 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -589,7 +589,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_SETPROCATTR; aad.info = name; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f1f7506a464d..03dbaef2f8e3 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -965,7 +965,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = op; aad.name = name; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index deab7c7e8dc0..504ba4015aa2 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -95,7 +95,7 @@ static int audit_iface(struct aa_profile *new, const char *name, struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; if (e) aad.iface.pos = e->pos - e->start; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index 2fe8613efe33..d06f57b74f77 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -52,7 +52,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, NONE); + COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); sa.aad = &aad; aad.op = OP_SETRLIMIT, aad.rlim.rlim = resource; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 1a04247e3a17..c04eea2bdb0a 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -469,7 +469,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, if (!a) { a = &stack_data; - COMMON_AUDIT_DATA_INIT(a, NONE); + COMMON_AUDIT_DATA_INIT(a, LSM_AUDIT_DATA_NONE); a->selinux_audit_data = &sad; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3bd8e1d268a..9f0384493009 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1427,7 +1427,7 @@ static int cred_has_capability(const struct cred *cred, u32 av = CAP_TO_MASK(cap); int rc; - COMMON_AUDIT_DATA_INIT(&ad, CAP); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); ad.selinux_audit_data = &sad; ad.tsk = current; ad.u.cap = cap; @@ -1499,7 +1499,7 @@ static inline int dentry_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1516,7 +1516,7 @@ static inline int path_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.u.path = *path; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1541,7 +1541,7 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.u.path = file->f_path; ad.selinux_audit_data = &sad; @@ -1582,7 +1582,7 @@ static int may_create(struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1637,7 +1637,7 @@ static int may_link(struct inode *dir, dsec = dir->i_security; isec = dentry->d_inode->i_security; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1685,7 +1685,7 @@ static inline int may_rename(struct inode *old_dir, old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); new_dsec = new_dir->i_security; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; @@ -2011,7 +2011,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } - COMMON_AUDIT_DATA_INIT(&ad, PATH); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; @@ -2135,7 +2135,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ - COMMON_AUDIT_DATA_INIT(&ad, INODE); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); @@ -2485,7 +2485,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) if (flags & MS_KERNMOUNT) return 0; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); @@ -2497,7 +2497,7 @@ static int selinux_sb_statfs(struct dentry *dentry) struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); @@ -2667,7 +2667,7 @@ static noinline int audit_inode_permission(struct inode *inode, struct inode_security_struct *isec = inode->i_security; int rc; - COMMON_AUDIT_DATA_INIT(&ad, INODE); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2797,7 +2797,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, if (!inode_owner_or_capable(inode)) return -EPERM; - COMMON_AUDIT_DATA_INIT(&ad, DENTRY); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); ad.selinux_audit_data = &sad; ad.u.dentry = dentry; @@ -3412,7 +3412,7 @@ static int selinux_kernel_module_request(char *kmod_name) sid = task_sid(current); - COMMON_AUDIT_DATA_INIT(&ad, KMOD); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_KMOD); ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; @@ -3793,7 +3793,7 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) if (sksec->sid == SECINITSID_KERNEL) return 0; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = sk; @@ -3901,7 +3901,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3936,7 +3936,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3998,7 +3998,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->dport = htons(snum); @@ -4095,7 +4095,7 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct lsm_network_audit net = {0,}; int err; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other; @@ -4128,7 +4128,7 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other->sk; @@ -4171,7 +4171,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net = {0,}; char *addrp; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4227,7 +4227,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!secmark_active && !peerlbl_active) return 0; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4584,7 +4584,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) return NF_DROP; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4684,7 +4684,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; sksec = sk->sk_security; - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4757,7 +4757,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; } - COMMON_AUDIT_DATA_INIT(&ad, NET); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4881,7 +4881,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, isec = ipc_perms->security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; @@ -4913,7 +4913,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -4940,7 +4940,7 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5002,7 +5002,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, return rc; } - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5035,7 +5035,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, isec = msq->q_perm.security; msec = msg->security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5062,7 +5062,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5089,7 +5089,7 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5158,7 +5158,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; @@ -5185,7 +5185,7 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, IPC); + COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; -- cgit v1.2.2 From 0972c74ecba4878baa5f97bb78b242c0eefacfb6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:42 -0400 Subject: apparmor: move task from common_audit_data to apparmor_audit_data apparmor is the only LSM that uses the common_audit_data tsk field. Instead of making all LSMs pay for the stack space move the aa usage into the apparmor_audit_data. Signed-off-by: Eric Paris --- security/apparmor/audit.c | 11 +++++++++-- security/apparmor/capability.c | 2 +- security/apparmor/include/audit.h | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c index cc3520d39a78..3ae28db5a64f 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -111,7 +111,7 @@ static const char *const aa_audit_type[] = { static void audit_pre(struct audit_buffer *ab, void *ca) { struct common_audit_data *sa = ca; - struct task_struct *tsk = sa->tsk ? sa->tsk : current; + struct task_struct *tsk = sa->aad->tsk ? sa->aad->tsk : current; if (aa_g_audit_header) { audit_log_format(ab, "apparmor="); @@ -149,6 +149,12 @@ static void audit_pre(struct audit_buffer *ab, void *ca) audit_log_format(ab, " name="); audit_log_untrustedstring(ab, sa->aad->name); } + + if (sa->aad->tsk) { + audit_log_format(ab, " pid=%d comm=", tsk->pid); + audit_log_untrustedstring(ab, tsk->comm); + } + } /** @@ -205,7 +211,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, aa_audit_msg(type, sa, cb); if (sa->aad->type == AUDIT_APPARMOR_KILL) - (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); + (void)send_sig_info(SIGKILL, NULL, + sa->aad->tsk ? sa->aad->tsk : current); if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) return complain_error(sa->aad->error); diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index 3ecb8b7d8502..b66a0e4a5693 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -67,8 +67,8 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, struct apparmor_audit_data aad = {0,}; COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_CAP); sa.aad = &aad; - sa.tsk = task; sa.u.cap = cap; + sa.aad->tsk = task; sa.aad->op = OP_CAPABLE; sa.aad->error = error; diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index 3868b1e5d5ba..4b7e18951aea 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -110,6 +110,7 @@ struct apparmor_audit_data { void *profile; const char *name; const char *info; + struct task_struct *tsk; union { void *target; struct { -- cgit v1.2.2 From b466066f9b648ccb6aa1e174f0389b7433e460fd Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: LSM: remove the task field from common_audit_data There are no legitimate users. Always use current and get back some stack space for the common_audit_data. Signed-off-by: Eric Paris --- security/lsm_audit.c | 8 ++------ security/selinux/hooks.c | 1 - 2 files changed, 2 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/security/lsm_audit.c b/security/lsm_audit.c index 90c129b0102f..e796d2517653 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -213,12 +213,8 @@ static void dump_common_audit_data(struct audit_buffer *ab, { struct task_struct *tsk = current; - if (a->tsk) - tsk = a->tsk; - if (tsk && tsk->pid) { - audit_log_format(ab, " pid=%d comm=", tsk->pid); - audit_log_untrustedstring(ab, tsk->comm); - } + audit_log_format(ab, " pid=%d comm=", tsk->pid); + audit_log_untrustedstring(ab, tsk->comm); switch (a->type) { case LSM_AUDIT_DATA_NONE: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9f0384493009..d79762946c6e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1429,7 +1429,6 @@ static int cred_has_capability(const struct cred *cred, COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); ad.selinux_audit_data = &sad; - ad.tsk = current; ad.u.cap = cap; switch (CAP_TO_INDEX(cap)) { -- cgit v1.2.2 From 07f62eb66c6626aa5653a0fcb34c9c040d0bd032 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: LSM: BUILD_BUG_ON if the common_audit_data union ever grows We did a lot of work to shrink the common_audit_data. Add a BUILD_BUG_ON so future programers (let's be honest, probably me) won't do something foolish like make it large again! Signed-off-by: Eric Paris --- security/lsm_audit.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'security') diff --git a/security/lsm_audit.c b/security/lsm_audit.c index e796d2517653..8d8d97dbb389 100644 --- a/security/lsm_audit.c +++ b/security/lsm_audit.c @@ -213,6 +213,13 @@ static void dump_common_audit_data(struct audit_buffer *ab, { struct task_struct *tsk = current; + /* + * To keep stack sizes in check force programers to notice if they + * start making this union too large! See struct lsm_network_audit + * as an example of how to deal with large data. + */ + BUILD_BUG_ON(sizeof(a->u) > sizeof(void *)*2); + audit_log_format(ab, " pid=%d comm=", tsk->pid); audit_log_untrustedstring(ab, tsk->comm); -- cgit v1.2.2 From 50c205f5e5c2e2af002fd4ef537ded79b90b1b56 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: LSM: do not initialize common_audit_data to 0 It isn't needed. If you don't set the type of the data associated with that type it is a pretty obvious programming bug. So why waste the cycles? Signed-off-by: Eric Paris --- security/apparmor/capability.c | 2 +- security/apparmor/file.c | 2 +- security/apparmor/ipc.c | 2 +- security/apparmor/lib.c | 2 +- security/apparmor/lsm.c | 2 +- security/apparmor/policy.c | 2 +- security/apparmor/policy_unpack.c | 2 +- security/apparmor/resource.c | 2 +- security/selinux/avc.c | 2 +- security/selinux/hooks.c | 68 +++++++++++++++++++-------------------- security/smack/smack.h | 2 +- 11 files changed, 44 insertions(+), 44 deletions(-) (limited to 'security') diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c index b66a0e4a5693..887a5e948945 100644 --- a/security/apparmor/capability.c +++ b/security/apparmor/capability.c @@ -65,7 +65,7 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_CAP); + sa.type = LSM_AUDIT_DATA_CAP; sa.aad = &aad; sa.u.cap = cap; sa.aad->tsk = task; diff --git a/security/apparmor/file.c b/security/apparmor/file.c index 6ab264ca85cc..cf19d4093ca4 100644 --- a/security/apparmor/file.c +++ b/security/apparmor/file.c @@ -108,7 +108,7 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, int type = AUDIT_APPARMOR_AUTO; struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op, aad.fs.request = request; diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index dba449b74db3..cf1071b14232 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -42,7 +42,7 @@ static int aa_audit_ptrace(struct aa_profile *profile, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_PTRACE; aad.target = target; diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index b11a2652f541..7430298116d6 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -66,7 +66,7 @@ void aa_info_message(const char *str) if (audit_enabled) { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.info = str; aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 4f7bc07b2dce..032daab449b0 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -589,7 +589,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, } else { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_SETPROCATTR; aad.info = name; diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 03dbaef2f8e3..421681c7c346 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -965,7 +965,7 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, { struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = op; aad.name = name; diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 504ba4015aa2..329b1fd30749 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -95,7 +95,7 @@ static int audit_iface(struct aa_profile *new, const char *name, struct aa_profile *profile = __aa_current_profile(); struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; if (e) aad.iface.pos = e->pos - e->start; diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c index d06f57b74f77..e1f3d7ef2c54 100644 --- a/security/apparmor/resource.c +++ b/security/apparmor/resource.c @@ -52,7 +52,7 @@ static int audit_resource(struct aa_profile *profile, unsigned int resource, struct common_audit_data sa; struct apparmor_audit_data aad = {0,}; - COMMON_AUDIT_DATA_INIT(&sa, LSM_AUDIT_DATA_NONE); + sa.type = LSM_AUDIT_DATA_NONE; sa.aad = &aad; aad.op = OP_SETRLIMIT, aad.rlim.rlim = resource; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index c04eea2bdb0a..cd91e25667d1 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -469,7 +469,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, if (!a) { a = &stack_data; - COMMON_AUDIT_DATA_INIT(a, LSM_AUDIT_DATA_NONE); + a->type = LSM_AUDIT_DATA_NONE; a->selinux_audit_data = &sad; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d79762946c6e..d9fa2489a551 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1427,7 +1427,7 @@ static int cred_has_capability(const struct cred *cred, u32 av = CAP_TO_MASK(cap); int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_CAP); + ad.type = LSM_AUDIT_DATA_CAP; ad.selinux_audit_data = &sad; ad.u.cap = cap; @@ -1498,7 +1498,7 @@ static inline int dentry_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1515,7 +1515,7 @@ static inline int path_has_perm(const struct cred *cred, struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = *path; ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); @@ -1540,7 +1540,7 @@ static int file_has_perm(const struct cred *cred, u32 sid = cred_sid(cred); int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; ad.selinux_audit_data = &sad; @@ -1581,7 +1581,7 @@ static int may_create(struct inode *dir, sid = tsec->sid; newsid = tsec->create_sid; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1636,7 +1636,7 @@ static int may_link(struct inode *dir, dsec = dir->i_security; isec = dentry->d_inode->i_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; ad.selinux_audit_data = &sad; @@ -1684,7 +1684,7 @@ static inline int may_rename(struct inode *old_dir, old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode); new_dsec = new_dir->i_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; @@ -2010,7 +2010,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) return rc; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_PATH); + ad.type = LSM_AUDIT_DATA_PATH; ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; @@ -2134,7 +2134,7 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); + ad.type = LSM_AUDIT_DATA_INODE; ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); @@ -2484,7 +2484,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) if (flags & MS_KERNMOUNT) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); @@ -2496,7 +2496,7 @@ static int selinux_sb_statfs(struct dentry *dentry) struct common_audit_data ad; struct selinux_audit_data sad = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); @@ -2666,7 +2666,7 @@ static noinline int audit_inode_permission(struct inode *inode, struct inode_security_struct *isec = inode->i_security; int rc; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_INODE); + ad.type = LSM_AUDIT_DATA_INODE; ad.selinux_audit_data = &sad; ad.u.inode = inode; @@ -2796,7 +2796,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, if (!inode_owner_or_capable(inode)) return -EPERM; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_DENTRY); + ad.type = LSM_AUDIT_DATA_DENTRY; ad.selinux_audit_data = &sad; ad.u.dentry = dentry; @@ -3411,7 +3411,7 @@ static int selinux_kernel_module_request(char *kmod_name) sid = task_sid(current); - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_KMOD); + ad.type = LSM_AUDIT_DATA_KMOD; ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; @@ -3792,7 +3792,7 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) if (sksec->sid == SECINITSID_KERNEL) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = sk; @@ -3900,7 +3900,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in snum, &sid); if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3935,7 +3935,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); @@ -3997,7 +3997,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->dport = htons(snum); @@ -4094,7 +4094,7 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct lsm_network_audit net = {0,}; int err; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other; @@ -4127,7 +4127,7 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other->sk; @@ -4170,7 +4170,7 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct lsm_network_audit net = {0,}; char *addrp; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4226,7 +4226,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) if (!secmark_active && !peerlbl_active) return 0; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; @@ -4583,7 +4583,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) return NF_DROP; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4683,7 +4683,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; sksec = sk->sk_security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4756,7 +4756,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, secmark_perm = PACKET__SEND; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_NET); + ad.type = LSM_AUDIT_DATA_NET; ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; @@ -4880,7 +4880,7 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, isec = ipc_perms->security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; @@ -4912,7 +4912,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -4939,7 +4939,7 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) isec = msq->q_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5001,7 +5001,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, return rc; } - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5034,7 +5034,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, isec = msq->q_perm.security; msec = msg->security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; @@ -5061,7 +5061,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5088,7 +5088,7 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) isec = shp->shm_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; @@ -5157,7 +5157,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; @@ -5184,7 +5184,7 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) isec = sma->sem_perm.security; - COMMON_AUDIT_DATA_INIT(&ad, LSM_AUDIT_DATA_IPC); + ad.type = LSM_AUDIT_DATA_IPC; ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; diff --git a/security/smack/smack.h b/security/smack/smack.h index 4ede719922ed..b61e75f224d4 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -319,7 +319,7 @@ void smack_log(char *subject_label, char *object_label, static inline void smk_ad_init(struct smk_audit_info *a, const char *func, char type) { - memset(a, 0, sizeof(*a)); + memset(&a->sad, 0, sizeof(a->sad)); a->a.type = type; a->a.smack_audit_data = &a->sad; a->a.smack_audit_data->function = func; -- cgit v1.2.2 From 1d3492927118d0ce1ea1ff3e007746699cba8f3e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: SELinux: remove auditdeny from selinux_audit_data It's just takin' up space. Signed-off-by: Eric Paris --- security/selinux/include/avc.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'security') diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index e4e50bb218ee..faa277729cb4 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -63,11 +63,6 @@ struct selinux_late_audit_data { * We collect this at the beginning or during an selinux security operation */ struct selinux_audit_data { - /* - * auditdeny is a bit tricky and unintuitive. See the - * comments in avc.c for it's meaning and usage. - */ - u32 auditdeny; struct selinux_late_audit_data *slad; }; @@ -145,9 +140,7 @@ static inline int avc_audit(u32 ssid, u32 tsid, struct common_audit_data *a, unsigned flags) { u32 audited, denied; - audited = avc_audit_required(requested, avd, result, - a ? a->selinux_audit_data->auditdeny : 0, - &denied); + audited = avc_audit_required(requested, avd, result, 0, &denied); if (likely(!audited)) return 0; return slow_avc_audit(ssid, tsid, tclass, -- cgit v1.2.2 From 899838b25f063a94594b1df6e0100aea1ec57fac Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 4 Apr 2012 15:01:43 -0400 Subject: SELinux: unify the selinux_audit_data and selinux_late_audit_data We no longer need the distinction. We only need data after we decide to do an audit. So turn the "late" audit data into just "data" and remove what we currently have as "data". Signed-off-by: Eric Paris --- security/selinux/avc.c | 31 ++++++++++--------- security/selinux/hooks.c | 67 ------------------------------------------ security/selinux/include/avc.h | 9 +----- 3 files changed, 16 insertions(+), 91 deletions(-) (limited to 'security') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index cd91e25667d1..c03a964ffde2 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -436,9 +436,9 @@ static void avc_audit_pre_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; audit_log_format(ab, "avc: %s ", - ad->selinux_audit_data->slad->denied ? "denied" : "granted"); - avc_dump_av(ab, ad->selinux_audit_data->slad->tclass, - ad->selinux_audit_data->slad->audited); + ad->selinux_audit_data->denied ? "denied" : "granted"); + avc_dump_av(ab, ad->selinux_audit_data->tclass, + ad->selinux_audit_data->audited); audit_log_format(ab, " for "); } @@ -452,9 +452,9 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a) { struct common_audit_data *ad = a; audit_log_format(ab, " "); - avc_dump_query(ab, ad->selinux_audit_data->slad->ssid, - ad->selinux_audit_data->slad->tsid, - ad->selinux_audit_data->slad->tclass); + avc_dump_query(ab, ad->selinux_audit_data->ssid, + ad->selinux_audit_data->tsid, + ad->selinux_audit_data->tclass); } /* This is the slow part of avc audit with big stack footprint */ @@ -464,13 +464,11 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, unsigned flags) { struct common_audit_data stack_data; - struct selinux_audit_data sad = {0,}; - struct selinux_late_audit_data slad; + struct selinux_audit_data sad; if (!a) { a = &stack_data; a->type = LSM_AUDIT_DATA_NONE; - a->selinux_audit_data = &sad; } /* @@ -484,14 +482,15 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, (flags & MAY_NOT_BLOCK)) return -ECHILD; - slad.tclass = tclass; - slad.requested = requested; - slad.ssid = ssid; - slad.tsid = tsid; - slad.audited = audited; - slad.denied = denied; + sad.tclass = tclass; + sad.requested = requested; + sad.ssid = ssid; + sad.tsid = tsid; + sad.audited = audited; + sad.denied = denied; + + a->selinux_audit_data = &sad; - a->selinux_audit_data->slad = &slad; common_lsm_audit(a, avc_audit_pre_callback, avc_audit_post_callback); return 0; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d9fa2489a551..2578de549ad7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1420,7 +1420,6 @@ static int cred_has_capability(const struct cred *cred, int cap, int audit) { struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct av_decision avd; u16 sclass; u32 sid = cred_sid(cred); @@ -1428,7 +1427,6 @@ static int cred_has_capability(const struct cred *cred, int rc; ad.type = LSM_AUDIT_DATA_CAP; - ad.selinux_audit_data = &sad; ad.u.cap = cap; switch (CAP_TO_INDEX(cap)) { @@ -1496,11 +1494,9 @@ static inline int dentry_has_perm(const struct cred *cred, { struct inode *inode = dentry->d_inode; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); } @@ -1513,11 +1509,9 @@ static inline int path_has_perm(const struct cred *cred, { struct inode *inode = path->dentry->d_inode; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = *path; - ad.selinux_audit_data = &sad; return inode_has_perm(cred, inode, av, &ad, 0); } @@ -1536,13 +1530,11 @@ static int file_has_perm(const struct cred *cred, struct file_security_struct *fsec = file->f_security; struct inode *inode = file->f_path.dentry->d_inode; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = cred_sid(cred); int rc; ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = file->f_path; - ad.selinux_audit_data = &sad; if (sid != fsec->sid) { rc = avc_has_perm(sid, fsec->sid, @@ -1572,7 +1564,6 @@ static int may_create(struct inode *dir, struct superblock_security_struct *sbsec; u32 sid, newsid; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; int rc; dsec = dir->i_security; @@ -1583,7 +1574,6 @@ static int may_create(struct inode *dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - ad.selinux_audit_data = &sad; rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR, DIR__ADD_NAME | DIR__SEARCH, @@ -1628,7 +1618,6 @@ static int may_link(struct inode *dir, { struct inode_security_struct *dsec, *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); u32 av; int rc; @@ -1638,7 +1627,6 @@ static int may_link(struct inode *dir, ad.type = LSM_AUDIT_DATA_DENTRY; ad.u.dentry = dentry; - ad.selinux_audit_data = &sad; av = DIR__SEARCH; av |= (kind ? DIR__REMOVE_NAME : DIR__ADD_NAME); @@ -1673,7 +1661,6 @@ static inline int may_rename(struct inode *old_dir, { struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); u32 av; int old_is_dir, new_is_dir; @@ -1685,7 +1672,6 @@ static inline int may_rename(struct inode *old_dir, new_dsec = new_dir->i_security; ad.type = LSM_AUDIT_DATA_DENTRY; - ad.selinux_audit_data = &sad; ad.u.dentry = old_dentry; rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR, @@ -1971,7 +1957,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) struct task_security_struct *new_tsec; struct inode_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct inode *inode = bprm->file->f_path.dentry->d_inode; int rc; @@ -2011,7 +1996,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) } ad.type = LSM_AUDIT_DATA_PATH; - ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) @@ -2101,7 +2085,6 @@ static inline void flush_unauthorized_files(const struct cred *cred, struct files_struct *files) { struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct file *file, *devnull = NULL; struct tty_struct *tty; struct fdtable *fdt; @@ -2135,7 +2118,6 @@ static inline void flush_unauthorized_files(const struct cred *cred, /* Revalidate access to inherited open files. */ ad.type = LSM_AUDIT_DATA_INODE; - ad.selinux_audit_data = &sad; spin_lock(&files->file_lock); for (;;) { @@ -2473,7 +2455,6 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) { const struct cred *cred = current_cred(); struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; int rc; rc = superblock_doinit(sb, data); @@ -2485,7 +2466,6 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data) return 0; ad.type = LSM_AUDIT_DATA_DENTRY; - ad.selinux_audit_data = &sad; ad.u.dentry = sb->s_root; return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad); } @@ -2494,10 +2474,8 @@ static int selinux_sb_statfs(struct dentry *dentry) { const struct cred *cred = current_cred(); struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; ad.type = LSM_AUDIT_DATA_DENTRY; - ad.selinux_audit_data = &sad; ad.u.dentry = dentry->d_sb->s_root; return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad); } @@ -2662,12 +2640,10 @@ static noinline int audit_inode_permission(struct inode *inode, unsigned flags) { struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct inode_security_struct *isec = inode->i_security; int rc; ad.type = LSM_AUDIT_DATA_INODE; - ad.selinux_audit_data = &sad; ad.u.inode = inode; rc = slow_avc_audit(current_sid(), isec->sid, isec->sclass, perms, @@ -2782,7 +2758,6 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, struct inode_security_struct *isec = inode->i_security; struct superblock_security_struct *sbsec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 newsid, sid = current_sid(); int rc = 0; @@ -2797,7 +2772,6 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name, return -EPERM; ad.type = LSM_AUDIT_DATA_DENTRY; - ad.selinux_audit_data = &sad; ad.u.dentry = dentry; rc = avc_has_perm(sid, isec->sid, isec->sclass, @@ -3407,12 +3381,10 @@ static int selinux_kernel_module_request(char *kmod_name) { u32 sid; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; sid = task_sid(current); ad.type = LSM_AUDIT_DATA_KMOD; - ad.selinux_audit_data = &sad; ad.u.kmod_name = kmod_name; return avc_has_perm(sid, SECINITSID_KERNEL, SECCLASS_SYSTEM, @@ -3785,7 +3757,6 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) { struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; u32 tsid = task_sid(task); @@ -3793,7 +3764,6 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) return 0; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = sk; @@ -3873,7 +3843,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in char *addrp; struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; @@ -3901,7 +3870,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in if (err) goto out; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); ad.u.net->family = family; @@ -3936,7 +3904,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in goto out; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sport = htons(snum); ad.u.net->family = family; @@ -3971,7 +3938,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (sksec->sclass == SECCLASS_TCP_SOCKET || sksec->sclass == SECCLASS_DCCP_SOCKET) { struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; @@ -3998,7 +3964,6 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->dport = htons(snum); ad.u.net->family = sk->sk_family; @@ -4090,12 +4055,10 @@ static int selinux_socket_unix_stream_connect(struct sock *sock, struct sk_security_struct *sksec_other = other->sk_security; struct sk_security_struct *sksec_new = newsk->sk_security; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; int err; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other; @@ -4124,11 +4087,9 @@ static int selinux_socket_unix_may_send(struct socket *sock, struct sk_security_struct *ssec = sock->sk->sk_security; struct sk_security_struct *osec = other->sk->sk_security; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->sk = other->sk; @@ -4166,12 +4127,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, struct sk_security_struct *sksec = sk->sk_security; u32 sk_sid = sksec->sid; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; char *addrp; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; ad.u.net->family = family; @@ -4201,7 +4160,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) u16 family = sk->sk_family; u32 sk_sid = sksec->sid; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; char *addrp; u8 secmark_active; @@ -4227,7 +4185,6 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) return 0; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = skb->skb_iif; ad.u.net->family = family; @@ -4565,7 +4522,6 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, char *addrp; u32 peer_sid; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; u8 secmark_active; u8 netlbl_active; @@ -4584,7 +4540,6 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, return NF_DROP; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; ad.u.net->family = family; @@ -4674,7 +4629,6 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, struct sock *sk = skb->sk; struct sk_security_struct *sksec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; char *addrp; u8 proto; @@ -4684,7 +4638,6 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, sksec = sk->sk_security; ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; ad.u.net->family = family; @@ -4709,7 +4662,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, u32 peer_sid; struct sock *sk; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; struct lsm_network_audit net = {0,}; char *addrp; u8 secmark_active; @@ -4757,7 +4709,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, } ad.type = LSM_AUDIT_DATA_NET; - ad.selinux_audit_data = &sad; ad.u.net = &net; ad.u.net->netif = ifindex; ad.u.net->family = family; @@ -4875,13 +4826,11 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms, { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = ipc_perms->security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = ipc_perms->key; return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); @@ -4902,7 +4851,6 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -4913,7 +4861,6 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq) isec = msq->q_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, @@ -4934,13 +4881,11 @@ static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = msq->q_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ, @@ -4980,7 +4925,6 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -5002,7 +4946,6 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg, } ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; /* Can this process write to the queue? */ @@ -5027,7 +4970,6 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, struct ipc_security_struct *isec; struct msg_security_struct *msec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = task_sid(target); int rc; @@ -5035,7 +4977,6 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg, msec = msg->security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = msq->q_perm.key; rc = avc_has_perm(sid, isec->sid, @@ -5051,7 +4992,6 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -5062,7 +5002,6 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp) isec = shp->shm_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM, @@ -5083,13 +5022,11 @@ static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = shp->shm_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = shp->shm_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_SHM, @@ -5147,7 +5084,6 @@ static int selinux_sem_alloc_security(struct sem_array *sma) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); int rc; @@ -5158,7 +5094,6 @@ static int selinux_sem_alloc_security(struct sem_array *sma) isec = sma->sem_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM, @@ -5179,13 +5114,11 @@ static int selinux_sem_associate(struct sem_array *sma, int semflg) { struct ipc_security_struct *isec; struct common_audit_data ad; - struct selinux_audit_data sad = {0,}; u32 sid = current_sid(); isec = sma->sem_perm.security; ad.type = LSM_AUDIT_DATA_IPC; - ad.selinux_audit_data = &sad; ad.u.ipc_id = sma->sem_perm.key; return avc_has_perm(sid, isec->sid, SECCLASS_SEM, diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index faa277729cb4..d97fadc4d963 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -49,7 +49,7 @@ struct avc_cache_stats { /* * We only need this data after we have decided to send an audit message. */ -struct selinux_late_audit_data { +struct selinux_audit_data { u32 ssid; u32 tsid; u16 tclass; @@ -59,13 +59,6 @@ struct selinux_late_audit_data { int result; }; -/* - * We collect this at the beginning or during an selinux security operation - */ -struct selinux_audit_data { - struct selinux_late_audit_data *slad; -}; - /* * AVC operations */ -- cgit v1.2.2 From 0b36e44cc680b355f0d1b34002b2a10c9e1cae60 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Wed, 7 Mar 2012 22:17:13 +0800 Subject: SELinux: replace weak GFP_ATOMIC to GFP_KERNEL in avc_add_callback avc_add_callback now only called from initcalls, so replace the weak GFP_ATOMIC to GFP_KERNEL, and mark this function __init to make a warning when not been called from initcalls. Signed-off-by: Wanlong Gao Signed-off-by: Eric Paris --- security/selinux/avc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index c03a964ffde2..5c1326e18836 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -510,7 +510,7 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, * @perms based on @tclass. Returns %0 on success or * -%ENOMEM if insufficient memory exists to add the callback. */ -int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, +int __init avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, u16 tclass, u32 perms, u32 *out_retained), u32 events, u32 ssid, u32 tsid, @@ -519,7 +519,7 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, struct avc_callback_node *c; int rc = 0; - c = kmalloc(sizeof(*c), GFP_ATOMIC); + c = kmalloc(sizeof(*c), GFP_KERNEL); if (!c) { rc = -ENOMEM; goto out; -- cgit v1.2.2 From 562c99f20d989f222138dddfd71e275bfb3665de Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Wed, 7 Mar 2012 22:17:14 +0800 Subject: SELinux: avc: remove the useless fields in avc_add_callback avc_add_callback now just used for registering reset functions in initcalls, and the callback functions just did reset operations. So, reducing the arguments to only one event is enough now. Signed-off-by: Wanlong Gao Signed-off-by: Eric Paris --- security/selinux/avc.c | 32 ++++++-------------------------- security/selinux/include/avc.h | 6 +----- security/selinux/netif.c | 6 ++---- security/selinux/netnode.c | 6 ++---- security/selinux/netport.c | 6 ++---- security/selinux/ss/services.c | 6 ++---- 6 files changed, 15 insertions(+), 47 deletions(-) (limited to 'security') diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 5c1326e18836..68d82daed257 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -65,14 +65,8 @@ struct avc_cache { }; struct avc_callback_node { - int (*callback) (u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, - u32 *out_retained); + int (*callback) (u32 event); u32 events; - u32 ssid; - u32 tsid; - u16 tclass; - u32 perms; struct avc_callback_node *next; }; @@ -499,22 +493,12 @@ noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass, * avc_add_callback - Register a callback for security events. * @callback: callback function * @events: security events - * @ssid: source security identifier or %SECSID_WILD - * @tsid: target security identifier or %SECSID_WILD - * @tclass: target security class - * @perms: permissions * - * Register a callback function for events in the set @events - * related to the SID pair (@ssid, @tsid) - * and the permissions @perms, interpreting - * @perms based on @tclass. Returns %0 on success or - * -%ENOMEM if insufficient memory exists to add the callback. + * Register a callback function for events in the set @events. + * Returns %0 on success or -%ENOMEM if insufficient memory + * exists to add the callback. */ -int __init avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, - u32 *out_retained), - u32 events, u32 ssid, u32 tsid, - u16 tclass, u32 perms) +int __init avc_add_callback(int (*callback)(u32 event), u32 events) { struct avc_callback_node *c; int rc = 0; @@ -527,9 +511,6 @@ int __init avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, c->callback = callback; c->events = events; - c->ssid = ssid; - c->tsid = tsid; - c->perms = perms; c->next = avc_callbacks; avc_callbacks = c; out: @@ -669,8 +650,7 @@ int avc_ss_reset(u32 seqno) for (c = avc_callbacks; c; c = c->next) { if (c->events & AVC_CALLBACK_RESET) { - tmprc = c->callback(AVC_CALLBACK_RESET, - 0, 0, 0, 0, NULL); + tmprc = c->callback(AVC_CALLBACK_RESET); /* save the first error encountered for the return value and continue processing the callbacks */ if (!rc) diff --git a/security/selinux/include/avc.h b/security/selinux/include/avc.h index d97fadc4d963..92d0ab561db8 100644 --- a/security/selinux/include/avc.h +++ b/security/selinux/include/avc.h @@ -170,11 +170,7 @@ u32 avc_policy_seqno(void); #define AVC_CALLBACK_AUDITDENY_ENABLE 64 #define AVC_CALLBACK_AUDITDENY_DISABLE 128 -int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, - u16 tclass, u32 perms, - u32 *out_retained), - u32 events, u32 ssid, u32 tsid, - u16 tclass, u32 perms); +int avc_add_callback(int (*callback)(u32 event), u32 events); /* Exported to selinuxfs */ int avc_get_hash_stats(char *page); diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 326f22cbe405..47a49d1a6f6a 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -252,8 +252,7 @@ static void sel_netif_flush(void) spin_unlock_bh(&sel_netif_lock); } -static int sel_netif_avc_callback(u32 event, u32 ssid, u32 tsid, - u16 class, u32 perms, u32 *retained) +static int sel_netif_avc_callback(u32 event) { if (event == AVC_CALLBACK_RESET) { sel_netif_flush(); @@ -292,8 +291,7 @@ static __init int sel_netif_init(void) register_netdevice_notifier(&sel_netif_netdev_notifier); - err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + err = avc_add_callback(sel_netif_avc_callback, AVC_CALLBACK_RESET); if (err) panic("avc_add_callback() failed, error %d\n", err); diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index 86365857c088..28f911cdd7c7 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -297,8 +297,7 @@ static void sel_netnode_flush(void) spin_unlock_bh(&sel_netnode_lock); } -static int sel_netnode_avc_callback(u32 event, u32 ssid, u32 tsid, - u16 class, u32 perms, u32 *retained) +static int sel_netnode_avc_callback(u32 event) { if (event == AVC_CALLBACK_RESET) { sel_netnode_flush(); @@ -320,8 +319,7 @@ static __init int sel_netnode_init(void) sel_netnode_hash[iter].size = 0; } - ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET); if (ret != 0) panic("avc_add_callback() failed, error %d\n", ret); diff --git a/security/selinux/netport.c b/security/selinux/netport.c index 7b9eb1faf68b..d35379781c2c 100644 --- a/security/selinux/netport.c +++ b/security/selinux/netport.c @@ -234,8 +234,7 @@ static void sel_netport_flush(void) spin_unlock_bh(&sel_netport_lock); } -static int sel_netport_avc_callback(u32 event, u32 ssid, u32 tsid, - u16 class, u32 perms, u32 *retained) +static int sel_netport_avc_callback(u32 event) { if (event == AVC_CALLBACK_RESET) { sel_netport_flush(); @@ -257,8 +256,7 @@ static __init int sel_netport_init(void) sel_netport_hash[iter].size = 0; } - ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + ret = avc_add_callback(sel_netport_avc_callback, AVC_CALLBACK_RESET); if (ret != 0) panic("avc_add_callback() failed, error %d\n", ret); diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 9b7e7ed54e7e..4321b8fc8863 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -3044,8 +3044,7 @@ out: static int (*aurule_callback)(void) = audit_update_lsm_rules; -static int aurule_avc_callback(u32 event, u32 ssid, u32 tsid, - u16 class, u32 perms, u32 *retained) +static int aurule_avc_callback(u32 event) { int err = 0; @@ -3058,8 +3057,7 @@ static int __init aurule_init(void) { int err; - err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET, - SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); + err = avc_add_callback(aurule_avc_callback, AVC_CALLBACK_RESET); if (err) panic("avc_add_callback() failed, error %d\n", err); -- cgit v1.2.2 From c737f8284cac91428f8fcc8281e69117fa16e887 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 5 Apr 2012 13:51:53 -0400 Subject: SELinux: remove unused common_audit_data in flush_unauthorized_files We don't need this variable and it just eats stack space. Remove it. Signed-off-by: Eric Paris --- security/selinux/hooks.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2578de549ad7..e94349b85bfe 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2084,7 +2084,6 @@ static int selinux_bprm_secureexec(struct linux_binprm *bprm) static inline void flush_unauthorized_files(const struct cred *cred, struct files_struct *files) { - struct common_audit_data ad; struct file *file, *devnull = NULL; struct tty_struct *tty; struct fdtable *fdt; @@ -2116,9 +2115,6 @@ static inline void flush_unauthorized_files(const struct cred *cred, no_tty(); /* Revalidate access to inherited open files. */ - - ad.type = LSM_AUDIT_DATA_INODE; - spin_lock(&files->file_lock); for (;;) { unsigned long set, i; -- cgit v1.2.2 From 259e5e6c75a910f3b5e656151dc602f53f9d7548 Mon Sep 17 00:00:00 2001 From: Andy Lutomirski Date: Thu, 12 Apr 2012 16:47:50 -0500 Subject: Add PR_{GET,SET}_NO_NEW_PRIVS to prevent execve from granting privs With this change, calling prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) disables privilege granting operations at execve-time. For example, a process will not be able to execute a setuid binary to change their uid or gid if this bit is set. The same is true for file capabilities. Additionally, LSM_UNSAFE_NO_NEW_PRIVS is defined to ensure that LSMs respect the requested behavior. To determine if the NO_NEW_PRIVS bit is set, a task may call prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); It returns 1 if set and 0 if it is not set. If any of the arguments are non-zero, it will return -1 and set errno to -EINVAL. (PR_SET_NO_NEW_PRIVS behaves similarly.) This functionality is desired for the proposed seccomp filter patch series. By using PR_SET_NO_NEW_PRIVS, it allows a task to modify the system call behavior for itself and its child tasks without being able to impact the behavior of a more privileged task. Another potential use is making certain privileged operations unprivileged. For example, chroot may be considered "safe" if it cannot affect privileged tasks. Note, this patch causes execve to fail when PR_SET_NO_NEW_PRIVS is set and AppArmor is in use. It is fixed in a subsequent patch. Signed-off-by: Andy Lutomirski Signed-off-by: Will Drewry Acked-by: Eric Paris Acked-by: Kees Cook v18: updated change desc v17: using new define values as per 3.4 Signed-off-by: James Morris --- security/apparmor/domain.c | 4 ++++ security/commoncap.c | 7 +++++-- security/selinux/hooks.c | 10 +++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 6327685c101e..18c88d06e881 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -360,6 +360,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; + /* XXX: no_new_privs is not usable with AppArmor yet */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) + return -EPERM; + cxt = bprm->cred->security; BUG_ON(!cxt); diff --git a/security/commoncap.c b/security/commoncap.c index 0cf4b53480a7..edd3918fac02 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -506,14 +506,17 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) skip: /* Don't let someone trace a set[ug]id/setpcap binary with the revised - * credentials unless they have the appropriate permit + * credentials unless they have the appropriate permit. + * + * In addition, if NO_NEW_PRIVS, then ensure we get no new privs. */ if ((new->euid != old->uid || new->egid != old->gid || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { /* downgrade; they get no more than they had, and maybe less */ - if (!capable(CAP_SETUID)) { + if (!capable(CAP_SETUID) || + (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) { new->euid = new->uid; new->egid = new->gid; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d85b793c9321..0b06685787b9 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2016,6 +2016,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) new_tsec->sid = old_tsec->exec_sid; /* Reset exec SID on execve. */ new_tsec->exec_sid = 0; + + /* + * Minimize confusion: if no_new_privs and a transition is + * explicitly requested, then fail the exec. + */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) + return -EPERM; } else { /* Check for a default transition on this program. */ rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2029,7 +2036,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) ad.selinux_audit_data = &sad; ad.u.path = bprm->file->f_path; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) + if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || + (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) new_tsec->sid = old_tsec->sid; if (new_tsec->sid == old_tsec->sid) { -- cgit v1.2.2 From c29bceb3967398cf2ac8bf8edf9634fdb722df7d Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 12 Apr 2012 16:47:51 -0500 Subject: Fix execve behavior apparmor for PR_{GET,SET}_NO_NEW_PRIVS Add support for AppArmor to explicitly fail requested domain transitions if NO_NEW_PRIVS is set and the task is not unconfined. Transitions from unconfined are still allowed because this always results in a reduction of privileges. Acked-by: Eric Paris Signed-off-by: Will Drewry Signed-off-by: John Johansen Signed-off-by: Andy Lutomirski v18: new acked-by, new description Signed-off-by: James Morris --- security/apparmor/domain.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 18c88d06e881..b81ea10a17a3 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -360,10 +360,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) if (bprm->cred_prepared) return 0; - /* XXX: no_new_privs is not usable with AppArmor yet */ - if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) - return -EPERM; - cxt = bprm->cred->security; BUG_ON(!cxt); @@ -398,6 +394,11 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) new_profile = find_attach(ns, &ns->base.profiles, name); if (!new_profile) goto cleanup; + /* + * NOTE: Domain transitions from unconfined are allowed + * even when no_new_privs is set because this aways results + * in a further reduction of permissions. + */ goto apply; } @@ -459,6 +460,16 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) /* fail exec */ error = -EACCES; + /* + * Policy has specified a domain transition, if no_new_privs then + * fail the exec. + */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { + aa_put_profile(new_profile); + error = -EPERM; + goto cleanup; + } + if (!new_profile) goto audit; @@ -613,6 +624,14 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) const char *target = NULL, *info = NULL; int error = 0; + /* + * Fail explicitly requested domain transitions if no_new_privs. + * There is no exception for unconfined as change_hat is not + * available. + */ + if (current->no_new_privs) + return -EPERM; + /* released below */ cred = get_current_cred(); cxt = cred->security; @@ -754,6 +773,18 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, cxt = cred->security; profile = aa_cred_profile(cred); + /* + * Fail explicitly requested domain transitions if no_new_privs + * and not unconfined. + * Domain transitions from unconfined are allowed even when + * no_new_privs is set because this aways results in a reduction + * of permissions. + */ + if (current->no_new_privs && !unconfined(profile)) { + put_cred(cred); + return -EPERM; + } + if (ns_name) { /* released below */ ns = aa_find_namespace(profile->ns, ns_name); -- cgit v1.2.2 From 389da25f93eea8ff64181ae7e3e87da68acaef2e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 16 Apr 2012 11:56:45 -0700 Subject: Yama: add additional ptrace scopes This expands the available Yama ptrace restrictions to include two more modes. Mode 2 requires CAP_SYS_PTRACE for PTRACE_ATTACH, and mode 3 completely disables PTRACE_ATTACH (and locks the sysctl). Signed-off-by: Kees Cook Signed-off-by: James Morris --- security/yama/yama_lsm.c | 62 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 11 deletions(-) (limited to 'security') diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 573723843a04..afb04cbb6971 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -18,7 +18,12 @@ #include #include -static int ptrace_scope = 1; +#define YAMA_SCOPE_DISABLED 0 +#define YAMA_SCOPE_RELATIONAL 1 +#define YAMA_SCOPE_CAPABILITY 2 +#define YAMA_SCOPE_NO_ATTACH 3 + +static int ptrace_scope = YAMA_SCOPE_RELATIONAL; /* describe a ptrace relationship for potential exception */ struct ptrace_relation { @@ -251,17 +256,32 @@ static int yama_ptrace_access_check(struct task_struct *child, return rc; /* require ptrace target be a child of ptracer on attach */ - if (mode == PTRACE_MODE_ATTACH && - ptrace_scope && - !task_is_descendant(current, child) && - !ptracer_exception_found(current, child) && - !capable(CAP_SYS_PTRACE)) - rc = -EPERM; + if (mode == PTRACE_MODE_ATTACH) { + switch (ptrace_scope) { + case YAMA_SCOPE_DISABLED: + /* No additional restrictions. */ + break; + case YAMA_SCOPE_RELATIONAL: + if (!task_is_descendant(current, child) && + !ptracer_exception_found(current, child) && + !capable(CAP_SYS_PTRACE)) + rc = -EPERM; + break; + case YAMA_SCOPE_CAPABILITY: + if (!capable(CAP_SYS_PTRACE)) + rc = -EPERM; + break; + case YAMA_SCOPE_NO_ATTACH: + default: + rc = -EPERM; + break; + } + } if (rc) { char name[sizeof(current->comm)]; - printk_ratelimited(KERN_NOTICE "ptrace of non-child" - " pid %d was attempted by: %s (pid %d)\n", + printk_ratelimited(KERN_NOTICE + "ptrace of pid %d was attempted by: %s (pid %d)\n", child->pid, get_task_comm(name, current), current->pid); @@ -279,8 +299,28 @@ static struct security_operations yama_ops = { }; #ifdef CONFIG_SYSCTL +static int yama_dointvec_minmax(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int rc; + + if (write && !capable(CAP_SYS_PTRACE)) + return -EPERM; + + rc = proc_dointvec_minmax(table, write, buffer, lenp, ppos); + if (rc) + return rc; + + /* Lock the max value if it ever gets set. */ + if (write && *(int *)table->data == *(int *)table->extra2) + table->extra1 = table->extra2; + + return rc; +} + static int zero; static int one = 1; +static int max_scope = YAMA_SCOPE_NO_ATTACH; struct ctl_path yama_sysctl_path[] = { { .procname = "kernel", }, @@ -294,9 +334,9 @@ static struct ctl_table yama_sysctl_table[] = { .data = &ptrace_scope, .maxlen = sizeof(int), .mode = 0644, - .proc_handler = proc_dointvec_minmax, + .proc_handler = yama_dointvec_minmax, .extra1 = &zero, - .extra2 = &one, + .extra2 = &max_scope, }, { } }; -- cgit v1.2.2 From 08162e6a23d476544adfe1164afe9ea8b34ab859 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 20 Apr 2012 16:35:24 +0300 Subject: Yama: remove an unused variable GCC complains that we don't use "one" any more after 389da25f93 "Yama: add additional ptrace scopes". security/yama/yama_lsm.c:322:12: warning: ?one? defined but not used [-Wunused-variable] Signed-off-by: Dan Carpenter Acked-by: Kees Cook Signed-off-by: James Morris --- security/yama/yama_lsm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index afb04cbb6971..c852f7472ad0 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -319,7 +319,6 @@ static int yama_dointvec_minmax(struct ctl_table *table, int write, } static int zero; -static int one = 1; static int max_scope = YAMA_SCOPE_NO_ATTACH; struct ctl_path yama_sysctl_path[] = { -- cgit v1.2.2 From 783291e6900292521a3895583785e0c04a56c5b3 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Thu, 17 Nov 2011 01:32:59 -0800 Subject: userns: Simplify the user_namespace by making userns->creator a kuid. - Transform userns->creator from a user_struct reference to a simple kuid_t, kgid_t pair. In cap_capable this allows the check to see if we are the creator of a namespace to become the classic suser style euid permission check. This allows us to remove the need for a struct cred in the mapping functions and still be able to dispaly the user namespace creators uid and gid as 0. - Remove the now unnecessary delayed_work in free_user_ns. All that is left for free_user_ns to do is to call kmem_cache_free and put_user_ns. Those functions can be called in any context so call them directly from free_user_ns removing the need for delayed work. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index 435d074853f3..f2399d8afbe0 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -76,8 +76,9 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, int cap, int audit) { for (;;) { - /* The creator of the user namespace has all caps. */ - if (targ_ns != &init_user_ns && targ_ns->creator == cred->user) + /* The owner of the user namespace has all caps. */ + if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, + make_kuid(cred->user_ns, cred->euid))) return 0; /* Do we have the necessary capabilities? */ -- cgit v1.2.2 From ae2975bc3476243b45a1e2344236d7920c268f38 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 14 Nov 2011 15:56:38 -0800 Subject: userns: Convert group_info values from gid_t to kgid_t. As a first step to converting struct cred to be all kuid_t and kgid_t values convert the group values stored in group_info to always be kgid_t values. Unless user namespaces are used this change should have no effect. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/keys/permission.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/permission.c b/security/keys/permission.c index e146cbd714bd..5442900d2929 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -53,7 +53,8 @@ int key_task_permission(const key_ref_t key_ref, const struct cred *cred, goto use_these_perms; } - ret = groups_search(cred->group_info, key->gid); + ret = groups_search(cred->group_info, + make_kgid(current_user_ns(), key->gid)); if (ret) { kperm = key->perm >> 8; goto use_these_perms; -- cgit v1.2.2 From 078de5f706ece36afd73bb4b8283314132d2dfdf Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 8 Feb 2012 07:00:08 -0800 Subject: userns: Store uid and gid values in struct cred with kuid_t and kgid_t types cred.h and a few trivial users of struct cred are changed. The rest of the users of struct cred are left for other patches as there are too many changes to make in one go and leave the change reviewable. If the user namespace is disabled and CONFIG_UIDGID_STRICT_TYPE_CHECKS are disabled the code will contiue to compile and behave correctly. Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index f2399d8afbe0..dbd465a59286 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -77,8 +77,7 @@ int cap_capable(const struct cred *cred, struct user_namespace *targ_ns, { for (;;) { /* The owner of the user namespace has all caps. */ - if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, - make_kuid(cred->user_ns, cred->euid))) + if (targ_ns != &init_user_ns && uid_eq(targ_ns->owner, cred->euid)) return 0; /* Do we have the necessary capabilities? */ -- cgit v1.2.2 From 18815a18085364d8514c0d0c4c986776cb74272c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 7 Feb 2012 16:45:47 -0800 Subject: userns: Convert capabilities related permsion checks - Use uid_eq when comparing kuids Use gid_eq when comparing kgids - Use make_kuid(user_ns, 0) to talk about the user_namespace root uid Acked-by: Serge Hallyn Signed-off-by: Eric W. Biederman --- security/commoncap.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) (limited to 'security') diff --git a/security/commoncap.c b/security/commoncap.c index dbd465a59286..ff9b113bb07c 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -472,19 +472,22 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) struct cred *new = bprm->cred; bool effective, has_cap = false; int ret; + kuid_t root_uid; effective = false; ret = get_file_caps(bprm, &effective, &has_cap); if (ret < 0) return ret; + root_uid = make_kuid(new->user_ns, 0); + if (!issecure(SECURE_NOROOT)) { /* * If the legacy file capability is set, then don't set privs * for a setuid root binary run by a non-root user. Do set it * for a root user just to cause least surprise to an admin. */ - if (has_cap && new->uid != 0 && new->euid == 0) { + if (has_cap && !uid_eq(new->uid, root_uid) && uid_eq(new->euid, root_uid)) { warn_setuid_and_fcaps_mixed(bprm->filename); goto skip; } @@ -495,12 +498,12 @@ int cap_bprm_set_creds(struct linux_binprm *bprm) * * If only the real uid is 0, we do not set the effective bit. */ - if (new->euid == 0 || new->uid == 0) { + if (uid_eq(new->euid, root_uid) || uid_eq(new->uid, root_uid)) { /* pP' = (cap_bset & ~0) | (pI & ~0) */ new->cap_permitted = cap_combine(old->cap_bset, old->cap_inheritable); } - if (new->euid == 0) + if (uid_eq(new->euid, root_uid)) effective = true; } skip: @@ -508,8 +511,8 @@ skip: /* Don't let someone trace a set[ug]id/setpcap binary with the revised * credentials unless they have the appropriate permit */ - if ((new->euid != old->uid || - new->egid != old->gid || + if ((!uid_eq(new->euid, old->uid) || + !gid_eq(new->egid, old->gid) || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { /* downgrade; they get no more than they had, and maybe less */ @@ -544,7 +547,7 @@ skip: */ if (!cap_isclear(new->cap_effective)) { if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || - new->euid != 0 || new->uid != 0 || + !uid_eq(new->euid, root_uid) || !uid_eq(new->uid, root_uid) || issecure(SECURE_NOROOT)) { ret = audit_log_bprm_fcaps(bprm, new, old); if (ret < 0) @@ -569,16 +572,17 @@ skip: int cap_bprm_secureexec(struct linux_binprm *bprm) { const struct cred *cred = current_cred(); + kuid_t root_uid = make_kuid(cred->user_ns, 0); - if (cred->uid != 0) { + if (!uid_eq(cred->uid, root_uid)) { if (bprm->cap_effective) return 1; if (!cap_isclear(cred->cap_permitted)) return 1; } - return (cred->euid != cred->uid || - cred->egid != cred->gid); + return (!uid_eq(cred->euid, cred->uid) || + !gid_eq(cred->egid, cred->gid)); } /** @@ -668,15 +672,21 @@ int cap_inode_removexattr(struct dentry *dentry, const char *name) */ static inline void cap_emulate_setxuid(struct cred *new, const struct cred *old) { - if ((old->uid == 0 || old->euid == 0 || old->suid == 0) && - (new->uid != 0 && new->euid != 0 && new->suid != 0) && + kuid_t root_uid = make_kuid(old->user_ns, 0); + + if ((uid_eq(old->uid, root_uid) || + uid_eq(old->euid, root_uid) || + uid_eq(old->suid, root_uid)) && + (!uid_eq(new->uid, root_uid) && + !uid_eq(new->euid, root_uid) && + !uid_eq(new->suid, root_uid)) && !issecure(SECURE_KEEP_CAPS)) { cap_clear(new->cap_permitted); cap_clear(new->cap_effective); } - if (old->euid == 0 && new->euid != 0) + if (uid_eq(old->euid, root_uid) && !uid_eq(new->euid, root_uid)) cap_clear(new->cap_effective); - if (old->euid != 0 && new->euid == 0) + if (!uid_eq(old->euid, root_uid) && uid_eq(new->euid, root_uid)) new->cap_effective = new->cap_permitted; } @@ -709,11 +719,12 @@ int cap_task_fix_setuid(struct cred *new, const struct cred *old, int flags) * if not, we might be a bit too harsh here. */ if (!issecure(SECURE_NO_SETUID_FIXUP)) { - if (old->fsuid == 0 && new->fsuid != 0) + kuid_t root_uid = make_kuid(old->user_ns, 0); + if (uid_eq(old->fsuid, root_uid) && !uid_eq(new->fsuid, root_uid)) new->cap_effective = cap_drop_fs_set(new->cap_effective); - if (old->fsuid != 0 && new->fsuid == 0) + if (!uid_eq(old->fsuid, root_uid) && uid_eq(new->fsuid, root_uid)) new->cap_effective = cap_raise_fs_set(new->cap_effective, new->cap_permitted); -- cgit v1.2.2 From d16cf20e2f2f13411eece7f7fb72c17d141c4a84 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 8 May 2012 19:45:28 +0200 Subject: netfilter: remove ip_queue support This patch removes ip_queue support which was marked as obsolete years ago. The nfnetlink_queue modules provides more advanced user-space packet queueing mechanism. This patch also removes capability code included in SELinux that refers to ip_queue. Otherwise, we break compilation. Several warning has been sent regarding this to the mailing list in the past month without anyone rising the hand to stop this with some strong argument. Signed-off-by: Pablo Neira Ayuso --- security/selinux/nlmsgtab.c | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'security') diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 0920ea3bf599..d309e7f472d8 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -70,12 +69,6 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_SETDCB, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, }; -static struct nlmsg_perm nlmsg_firewall_perms[] = -{ - { IPQM_MODE, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, - { IPQM_VERDICT, NETLINK_FIREWALL_SOCKET__NLMSG_WRITE }, -}; - static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, @@ -145,12 +138,6 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) sizeof(nlmsg_route_perms)); break; - case SECCLASS_NETLINK_FIREWALL_SOCKET: - case SECCLASS_NETLINK_IP6FW_SOCKET: - err = nlmsg_perm(nlmsg_type, perm, nlmsg_firewall_perms, - sizeof(nlmsg_firewall_perms)); - break; - case SECCLASS_NETLINK_TCPDIAG_SOCKET: err = nlmsg_perm(nlmsg_type, perm, nlmsg_tcpdiag_perms, sizeof(nlmsg_tcpdiag_perms)); -- cgit v1.2.2 From f0894940aed13b21f363a411c7ec57358827ad87 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Move the key config into security/keys/Kconfig Move the key config into security/keys/Kconfig as there are going to be a lot of key-related options. Signed-off-by: David Howells Acked-by: Mimi Zohar --- security/Kconfig | 68 +----------------------------------------------- security/keys/Kconfig | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 67 deletions(-) create mode 100644 security/keys/Kconfig (limited to 'security') diff --git a/security/Kconfig b/security/Kconfig index ccc61f8006b2..e9c6ac724fef 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -4,73 +4,7 @@ menu "Security options" -config KEYS - bool "Enable access key retention support" - help - This option provides support for retaining authentication tokens and - access keys in the kernel. - - It also includes provision of methods by which such keys might be - associated with a process so that network filesystems, encryption - support and the like can find them. - - Furthermore, a special type of key is available that acts as keyring: - a searchable sequence of keys. Each process is equipped with access - to five standard keyrings: UID-specific, GID-specific, session, - process and thread. - - If you are unsure as to whether this is required, answer N. - -config TRUSTED_KEYS - tristate "TRUSTED KEYS" - depends on KEYS && TCG_TPM - select CRYPTO - select CRYPTO_HMAC - select CRYPTO_SHA1 - help - This option provides support for creating, sealing, and unsealing - keys in the kernel. Trusted keys are random number symmetric keys, - generated and RSA-sealed by the TPM. The TPM only unseals the keys, - if the boot PCRs and other criteria match. Userspace will only ever - see encrypted blobs. - - If you are unsure as to whether this is required, answer N. - -config ENCRYPTED_KEYS - tristate "ENCRYPTED KEYS" - depends on KEYS - select CRYPTO - select CRYPTO_HMAC - select CRYPTO_AES - select CRYPTO_CBC - select CRYPTO_SHA256 - select CRYPTO_RNG - help - This option provides support for create/encrypting/decrypting keys - in the kernel. Encrypted keys are kernel generated random numbers, - which are encrypted/decrypted with a 'master' symmetric key. The - 'master' key can be either a trusted-key or user-key type. - Userspace only ever sees/stores encrypted blobs. - - If you are unsure as to whether this is required, answer N. - -config KEYS_DEBUG_PROC_KEYS - bool "Enable the /proc/keys file by which keys may be viewed" - depends on KEYS - help - This option turns on support for the /proc/keys file - through which - can be listed all the keys on the system that are viewable by the - reading process. - - The only keys included in the list are those that grant View - permission to the reading process whether or not it possesses them. - Note that LSM security checks are still performed, and may further - filter out keys that the current process is not authorised to view. - - Only key attributes are listed here; key payloads are not included in - the resulting table. - - If you are unsure as to whether this is required, answer N. +source security/keys/Kconfig config SECURITY_DMESG_RESTRICT bool "Restrict unprivileged access to the kernel syslog" diff --git a/security/keys/Kconfig b/security/keys/Kconfig new file mode 100644 index 000000000000..a90d6d300dbd --- /dev/null +++ b/security/keys/Kconfig @@ -0,0 +1,71 @@ +# +# Key management configuration +# + +config KEYS + bool "Enable access key retention support" + help + This option provides support for retaining authentication tokens and + access keys in the kernel. + + It also includes provision of methods by which such keys might be + associated with a process so that network filesystems, encryption + support and the like can find them. + + Furthermore, a special type of key is available that acts as keyring: + a searchable sequence of keys. Each process is equipped with access + to five standard keyrings: UID-specific, GID-specific, session, + process and thread. + + If you are unsure as to whether this is required, answer N. + +config TRUSTED_KEYS + tristate "TRUSTED KEYS" + depends on KEYS && TCG_TPM + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_SHA1 + help + This option provides support for creating, sealing, and unsealing + keys in the kernel. Trusted keys are random number symmetric keys, + generated and RSA-sealed by the TPM. The TPM only unseals the keys, + if the boot PCRs and other criteria match. Userspace will only ever + see encrypted blobs. + + If you are unsure as to whether this is required, answer N. + +config ENCRYPTED_KEYS + tristate "ENCRYPTED KEYS" + depends on KEYS + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_AES + select CRYPTO_CBC + select CRYPTO_SHA256 + select CRYPTO_RNG + help + This option provides support for create/encrypting/decrypting keys + in the kernel. Encrypted keys are kernel generated random numbers, + which are encrypted/decrypted with a 'master' symmetric key. The + 'master' key can be either a trusted-key or user-key type. + Userspace only ever sees/stores encrypted blobs. + + If you are unsure as to whether this is required, answer N. + +config KEYS_DEBUG_PROC_KEYS + bool "Enable the /proc/keys file by which keys may be viewed" + depends on KEYS + help + This option turns on support for the /proc/keys file - through which + can be listed all the keys on the system that are viewable by the + reading process. + + The only keys included in the list are those that grant View + permission to the reading process whether or not it possesses them. + Note that LSM security checks are still performed, and may further + filter out keys that the current process is not authorised to view. + + Only key attributes are listed here; key payloads are not included in + the resulting table. + + If you are unsure as to whether this is required, answer N. -- cgit v1.2.2 From 9f7ce8e249ab761c7ed753059cb16319ede41762 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Reorganise keys Makefile Reorganise the keys directory Makefile to put all the core bits together and the type-specific bits after. Signed-off-by: David Howells Acked-by: Mimi Zohar --- security/keys/Makefile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/keys/Makefile b/security/keys/Makefile index a56f1ffdc64d..504aaa008388 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -2,6 +2,9 @@ # Makefile for key management # +# +# Core +# obj-y := \ gc.o \ key.o \ @@ -12,9 +15,12 @@ obj-y := \ request_key.o \ request_key_auth.o \ user_defined.o - -obj-$(CONFIG_TRUSTED_KEYS) += trusted.o -obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ obj-$(CONFIG_KEYS_COMPAT) += compat.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o + +# +# Key types +# +obj-$(CONFIG_TRUSTED_KEYS) += trusted.o +obj-$(CONFIG_ENCRYPTED_KEYS) += encrypted-keys/ -- cgit v1.2.2 From 1eb1bcf5bfad001128293b86d891c9d6f2f27333 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Announce key type (un)registration Announce the (un)registration of a key type in the core key code rather than in the callers. Signed-off-by: David Howells Acked-by: Mimi Zohar --- security/keys/key.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'security') diff --git a/security/keys/key.c b/security/keys/key.c index 06783cffb3af..dc628941ecd3 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -980,6 +980,8 @@ int register_key_type(struct key_type *ktype) /* store the type */ list_add(&ktype->link, &key_types_list); + + pr_notice("Key type %s registered\n", ktype->name); ret = 0; out: @@ -1002,6 +1004,7 @@ void unregister_key_type(struct key_type *ktype) list_del_init(&ktype->link); downgrade_write(&key_types_sem); key_gc_keytype(ktype); + pr_notice("Key type %s unregistered\n", ktype->name); up_read(&key_types_sem); } EXPORT_SYMBOL(unregister_key_type); -- cgit v1.2.2 From 65d87fe68abf2fc226a9e96be61160f65d6b4680 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Perform RCU synchronisation on keys prior to key destruction Make the keys garbage collector invoke synchronize_rcu() prior to destroying keys with a zero usage count. This means that a key can be examined under the RCU read lock in the safe knowledge that it won't get deallocated until after the lock is released - even if its usage count becomes zero whilst we're looking at it. This is useful in keyring search vs key link. Consider a keyring containing a link to a key. That link can be replaced in-place in the keyring without requiring an RCU copy-and-replace on the keyring contents without breaking a search underway on that keyring when the displaced key is released, provided the key is actually destroyed only after the RCU read lock held by the search algorithm is released. This permits __key_link() to replace a key without having to reallocate the key payload. A key gets replaced if a new key being linked into a keyring has the same type and description. Signed-off-by: David Howells Acked-by: Jeff Layton --- security/keys/gc.c | 73 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 29 deletions(-) (limited to 'security') diff --git a/security/keys/gc.c b/security/keys/gc.c index a42b45531aac..27610bf72195 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -168,38 +168,45 @@ do_gc: } /* - * Garbage collect an unreferenced, detached key + * Garbage collect a list of unreferenced, detached keys */ -static noinline void key_gc_unused_key(struct key *key) +static noinline void key_gc_unused_keys(struct list_head *keys) { - key_check(key); - - security_key_free(key); - - /* deal with the user's key tracking and quota */ - if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { - spin_lock(&key->user->lock); - key->user->qnkeys--; - key->user->qnbytes -= key->quotalen; - spin_unlock(&key->user->lock); - } + while (!list_empty(keys)) { + struct key *key = + list_entry(keys->next, struct key, graveyard_link); + list_del(&key->graveyard_link); + + kdebug("- %u", key->serial); + key_check(key); + + security_key_free(key); + + /* deal with the user's key tracking and quota */ + if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) { + spin_lock(&key->user->lock); + key->user->qnkeys--; + key->user->qnbytes -= key->quotalen; + spin_unlock(&key->user->lock); + } - atomic_dec(&key->user->nkeys); - if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) - atomic_dec(&key->user->nikeys); + atomic_dec(&key->user->nkeys); + if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) + atomic_dec(&key->user->nikeys); - key_user_put(key->user); + key_user_put(key->user); - /* now throw away the key memory */ - if (key->type->destroy) - key->type->destroy(key); + /* now throw away the key memory */ + if (key->type->destroy) + key->type->destroy(key); - kfree(key->description); + kfree(key->description); #ifdef KEY_DEBUGGING - key->magic = KEY_DEBUG_MAGIC_X; + key->magic = KEY_DEBUG_MAGIC_X; #endif - kmem_cache_free(key_jar, key); + kmem_cache_free(key_jar, key); + } } /* @@ -211,6 +218,7 @@ static noinline void key_gc_unused_key(struct key *key) */ static void key_garbage_collector(struct work_struct *work) { + static LIST_HEAD(graveyard); static u8 gc_state; /* Internal persistent state */ #define KEY_GC_REAP_AGAIN 0x01 /* - Need another cycle */ #define KEY_GC_REAPING_LINKS 0x02 /* - We need to reap links */ @@ -316,15 +324,22 @@ maybe_resched: key_schedule_gc(new_timer); } - if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2)) { - /* Make sure everyone revalidates their keys if we marked a - * bunch as being dead and make sure all keyring ex-payloads - * are destroyed. + if (unlikely(gc_state & KEY_GC_REAPING_DEAD_2) || + !list_empty(&graveyard)) { + /* Make sure that all pending keyring payload destructions are + * fulfilled and that people aren't now looking at dead or + * dying keys that they don't have a reference upon or a link + * to. */ - kdebug("dead sync"); + kdebug("gc sync"); synchronize_rcu(); } + if (!list_empty(&graveyard)) { + kdebug("gc keys"); + key_gc_unused_keys(&graveyard); + } + if (unlikely(gc_state & (KEY_GC_REAPING_DEAD_1 | KEY_GC_REAPING_DEAD_2))) { if (!(gc_state & KEY_GC_FOUND_DEAD_KEY)) { @@ -359,7 +374,7 @@ found_unreferenced_key: rb_erase(&key->serial_node, &key_serial_tree); spin_unlock(&key_serial_lock); - key_gc_unused_key(key); + list_add_tail(&key->graveyard_link, &graveyard); gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched; -- cgit v1.2.2 From 233e4735f2a45d9e641c2488b8d7afeb1f377dac Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Permit in-place link replacement in keyring list Make use of the previous patch that makes the garbage collector perform RCU synchronisation before destroying defunct keys. Key pointers can now be replaced in-place without creating a new keyring payload and replacing the whole thing as the discarded keys will not be destroyed until all currently held RCU read locks are released. If the keyring payload space needs to be expanded or contracted, then a replacement will still need allocating, and the original will still have to be freed by RCU. Signed-off-by: David Howells --- security/keys/gc.c | 2 +- security/keys/keyring.c | 95 +++++++++++++++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 40 deletions(-) (limited to 'security') diff --git a/security/keys/gc.c b/security/keys/gc.c index 27610bf72195..adddaa258d50 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -148,7 +148,7 @@ static void key_gc_keyring(struct key *keyring, time_t limit) loop = klist->nkeys; smp_rmb(); for (loop--; loop >= 0; loop--) { - key = klist->keys[loop]; + key = rcu_dereference(klist->keys[loop]); if (test_bit(KEY_FLAG_DEAD, &key->flags) || (key->expiry > 0 && key->expiry <= limit)) goto do_gc; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d605f75292e4..459b3cc347f2 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -25,6 +25,11 @@ (keyring)->payload.subscriptions, \ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) +#define rcu_deref_link_locked(klist, index, keyring) \ + (rcu_dereference_protected( \ + (klist)->keys[index], \ + rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) + #define KEY_LINK_FIXQUOTA 1UL /* @@ -138,6 +143,11 @@ static int keyring_match(const struct key *keyring, const void *description) /* * Clean up a keyring when it is destroyed. Unpublish its name if it had one * and dispose of its data. + * + * The garbage collector detects the final key_put(), removes the keyring from + * the serial number tree and then does RCU synchronisation before coming here, + * so we shouldn't need to worry about code poking around here with the RCU + * readlock held by this time. */ static void keyring_destroy(struct key *keyring) { @@ -154,11 +164,10 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } - klist = rcu_dereference_check(keyring->payload.subscriptions, - atomic_read(&keyring->usage) == 0); + klist = rcu_access_pointer(keyring->payload.subscriptions); if (klist) { for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); + key_put(rcu_access_pointer(klist->keys[loop])); kfree(klist); } } @@ -214,7 +223,8 @@ static long keyring_read(const struct key *keyring, ret = -EFAULT; for (loop = 0; loop < klist->nkeys; loop++) { - key = klist->keys[loop]; + key = rcu_deref_link_locked(klist, loop, + keyring); tmp = sizeof(key_serial_t); if (tmp > buflen) @@ -383,7 +393,7 @@ descend: nkeys = keylist->nkeys; smp_rmb(); for (kix = 0; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); kflags = key->flags; /* ignore keys not of this type */ @@ -426,7 +436,7 @@ ascend: nkeys = keylist->nkeys; smp_rmb(); for (; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); if (key->type != &key_type_keyring) continue; @@ -531,8 +541,7 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, nkeys = klist->nkeys; smp_rmb(); for (loop = 0; loop < nkeys ; loop++) { - key = klist->keys[loop]; - + key = rcu_dereference(klist->keys[loop]); if (key->type == ktype && (!key->type->match || key->type->match(key, description)) && @@ -654,7 +663,7 @@ ascend: nkeys = keylist->nkeys; smp_rmb(); for (; kix < nkeys; kix++) { - key = keylist->keys[kix]; + key = rcu_dereference(keylist->keys[kix]); if (key == A) goto cycle_detected; @@ -711,7 +720,7 @@ static void keyring_unlink_rcu_disposal(struct rcu_head *rcu) container_of(rcu, struct keyring_list, rcu); if (klist->delkey != USHRT_MAX) - key_put(klist->keys[klist->delkey]); + key_put(rcu_access_pointer(klist->keys[klist->delkey])); kfree(klist); } @@ -749,24 +758,16 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, /* see if there's a matching key we can displace */ if (klist && klist->nkeys > 0) { for (loop = klist->nkeys - 1; loop >= 0; loop--) { - if (klist->keys[loop]->type == type && - strcmp(klist->keys[loop]->description, - description) == 0 - ) { - /* found a match - we'll replace this one with - * the new key */ - size = sizeof(struct key *) * klist->maxkeys; - size += sizeof(*klist); - BUG_ON(size > PAGE_SIZE); - - ret = -ENOMEM; - nklist = kmemdup(klist, size, GFP_KERNEL); - if (!nklist) - goto error_sem; - - /* note replacement slot */ - klist->delkey = nklist->delkey = loop; - prealloc = (unsigned long)nklist; + struct key *key = rcu_deref_link_locked(klist, loop, + keyring); + if (key->type == type && + strcmp(key->description, description) == 0) { + /* Found a match - we'll replace the link with + * one to the new key. We record the slot + * position. + */ + klist->delkey = loop; + prealloc = 0; goto done; } } @@ -780,7 +781,7 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, if (klist && klist->nkeys < klist->maxkeys) { /* there's sufficient slack space to append directly */ - nklist = NULL; + klist->delkey = klist->nkeys; prealloc = KEY_LINK_FIXQUOTA; } else { /* grow the key list */ @@ -813,10 +814,10 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, } /* add the key into the new space */ - nklist->keys[nklist->delkey] = NULL; + RCU_INIT_POINTER(nklist->keys[nklist->delkey], NULL); + prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA; } - prealloc = (unsigned long)nklist | KEY_LINK_FIXQUOTA; done: *_prealloc = prealloc; kleave(" = 0"); @@ -862,6 +863,7 @@ void __key_link(struct key *keyring, struct key *key, unsigned long *_prealloc) { struct keyring_list *klist, *nklist; + struct key *discard; nklist = (struct keyring_list *)(*_prealloc & ~KEY_LINK_FIXQUOTA); *_prealloc = 0; @@ -875,10 +877,10 @@ void __key_link(struct key *keyring, struct key *key, /* there's a matching key we can displace or an empty slot in a newly * allocated list we can fill */ if (nklist) { - kdebug("replace %hu/%hu/%hu", + kdebug("reissue %hu/%hu/%hu", nklist->delkey, nklist->nkeys, nklist->maxkeys); - nklist->keys[nklist->delkey] = key; + RCU_INIT_POINTER(nklist->keys[nklist->delkey], key); rcu_assign_pointer(keyring->payload.subscriptions, nklist); @@ -889,9 +891,23 @@ void __key_link(struct key *keyring, struct key *key, klist->delkey, klist->nkeys, klist->maxkeys); call_rcu(&klist->rcu, keyring_unlink_rcu_disposal); } + } else if (klist->delkey < klist->nkeys) { + kdebug("replace %hu/%hu/%hu", + klist->delkey, klist->nkeys, klist->maxkeys); + + discard = rcu_dereference_protected( + klist->keys[klist->delkey], + rwsem_is_locked(&keyring->sem)); + rcu_assign_pointer(klist->keys[klist->delkey], key); + /* The garbage collector will take care of RCU + * synchronisation */ + key_put(discard); } else { /* there's sufficient slack space to append directly */ - klist->keys[klist->nkeys] = key; + kdebug("append %hu/%hu/%hu", + klist->delkey, klist->nkeys, klist->maxkeys); + + RCU_INIT_POINTER(klist->keys[klist->delkey], key); smp_wmb(); klist->nkeys++; } @@ -998,7 +1014,7 @@ int key_unlink(struct key *keyring, struct key *key) if (klist) { /* search the keyring for the key */ for (loop = 0; loop < klist->nkeys; loop++) - if (klist->keys[loop] == key) + if (rcu_access_pointer(klist->keys[loop]) == key) goto key_is_present; } @@ -1061,7 +1077,7 @@ static void keyring_clear_rcu_disposal(struct rcu_head *rcu) klist = container_of(rcu, struct keyring_list, rcu); for (loop = klist->nkeys - 1; loop >= 0; loop--) - key_put(klist->keys[loop]); + key_put(rcu_access_pointer(klist->keys[loop])); kfree(klist); } @@ -1161,7 +1177,8 @@ void keyring_gc(struct key *keyring, time_t limit) /* work out how many subscriptions we're keeping */ keep = 0; for (loop = klist->nkeys - 1; loop >= 0; loop--) - if (!key_is_dead(klist->keys[loop], limit)) + if (!key_is_dead(rcu_deref_link_locked(klist, loop, keyring), + limit)) keep++; if (keep == klist->nkeys) @@ -1182,11 +1199,11 @@ void keyring_gc(struct key *keyring, time_t limit) */ keep = 0; for (loop = klist->nkeys - 1; loop >= 0; loop--) { - key = klist->keys[loop]; + key = rcu_deref_link_locked(klist, loop, keyring); if (!key_is_dead(key, limit)) { if (keep >= max) goto discard_new; - new->keys[keep++] = key_get(key); + RCU_INIT_POINTER(new->keys[keep++], key_get(key)); } } new->nkeys = keep; -- cgit v1.2.2 From 31d5a79d7f3d436da176a78ebc12d53c06da402e Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Do LRU discard in full keyrings Do an LRU discard in keyrings that are full rather than returning ENFILE. To perform this, a time_t is added to the key struct and updated by the creation of a link to a key and by a key being found as the result of a search. At the completion of a successful search, the keyrings in the path between the root of the search and the first found link to it also have their last-used times updated. Note that discarding a link to a key from a keyring does not necessarily destroy the key as there may be references held by other places. An alternate discard method that might suffice is to perform FIFO discard from the keyring, using the spare 2-byte hole in the keylist header as the index of the next link to be discarded. This is useful when using a keyring as a cache for DNS results or foreign filesystem IDs. This can be tested by the following. As root do: echo 1000 >/proc/sys/kernel/keys/root_maxkeys kr=`keyctl newring foo @s` for ((i=0; i<2000; i++)); do keyctl add user a$i a $kr; done Without this patch ENFILE should be reported when the keyring fills up. With this patch, the keyring discards keys in an LRU fashion. Note that the stored LRU time has a granularity of 1s. After doing this, /proc/key-users can be observed and should show that most of the 2000 keys have been discarded: [root@andromeda ~]# cat /proc/key-users 0: 517 516/516 513/1000 5249/20000 The "513/1000" here is the number of quota-accounted keys present for this user out of the maximum permitted. In /proc/keys, the keyring shows the number of keys it has and the number of slots it has allocated: [root@andromeda ~]# grep foo /proc/keys 200c64c4 I--Q-- 1 perm 3b3f0000 0 0 keyring foo: 509/509 The maximum is (PAGE_SIZE - header) / key pointer size. That's typically 509 on a 64-bit system and 1020 on a 32-bit system. Signed-off-by: David Howells --- security/keys/keyring.c | 47 +++++++++++++++++++++++++++++++++++++------- security/keys/process_keys.c | 2 ++ 2 files changed, 42 insertions(+), 7 deletions(-) (limited to 'security') diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 459b3cc347f2..89d02cfb00c2 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -30,6 +30,10 @@ (klist)->keys[index], \ rwsem_is_locked((struct rw_semaphore *)&(keyring)->sem))) +#define MAX_KEYRING_LINKS \ + min_t(size_t, USHRT_MAX - 1, \ + ((PAGE_SIZE - sizeof(struct keyring_list)) / sizeof(struct key *))) + #define KEY_LINK_FIXQUOTA 1UL /* @@ -319,6 +323,8 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, bool no_state_check) { struct { + /* Need a separate keylist pointer for RCU purposes */ + struct key *keyring; struct keyring_list *keylist; int kix; } stack[KEYRING_SEARCH_MAX_DEPTH]; @@ -451,6 +457,7 @@ ascend: continue; /* stack the current position */ + stack[sp].keyring = keyring; stack[sp].keylist = keylist; stack[sp].kix = kix; sp++; @@ -466,6 +473,7 @@ not_this_keyring: if (sp > 0) { /* resume the processing of a keyring higher up in the tree */ sp--; + keyring = stack[sp].keyring; keylist = stack[sp].keylist; kix = stack[sp].kix + 1; goto ascend; @@ -477,6 +485,10 @@ not_this_keyring: /* we found a viable match */ found: atomic_inc(&key->usage); + key->last_used_at = now.tv_sec; + keyring->last_used_at = now.tv_sec; + while (sp > 0) + stack[--sp].keyring->last_used_at = now.tv_sec; key_check(key); key_ref = make_key_ref(key, possessed); error_2: @@ -558,6 +570,8 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, found: atomic_inc(&key->usage); + keyring->last_used_at = key->last_used_at = + current_kernel_time().tv_sec; rcu_read_unlock(); return make_key_ref(key, possessed); } @@ -611,6 +625,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) * (ie. it has a zero usage count) */ if (!atomic_inc_not_zero(&keyring->usage)) continue; + keyring->last_used_at = current_kernel_time().tv_sec; goto out; } } @@ -734,8 +749,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, struct keyring_list *klist, *nklist; unsigned long prealloc; unsigned max; + time_t lowest_lru; size_t size; - int loop, ret; + int loop, lru, ret; kenter("%d,%s,%s,", key_serial(keyring), type->name, description); @@ -756,7 +772,9 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, klist = rcu_dereference_locked_keyring(keyring); /* see if there's a matching key we can displace */ + lru = -1; if (klist && klist->nkeys > 0) { + lowest_lru = TIME_T_MAX; for (loop = klist->nkeys - 1; loop >= 0; loop--) { struct key *key = rcu_deref_link_locked(klist, loop, keyring); @@ -770,9 +788,23 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, prealloc = 0; goto done; } + if (key->last_used_at < lowest_lru) { + lowest_lru = key->last_used_at; + lru = loop; + } } } + /* If the keyring is full then do an LRU discard */ + if (klist && + klist->nkeys == klist->maxkeys && + klist->maxkeys >= MAX_KEYRING_LINKS) { + kdebug("LRU discard %d\n", lru); + klist->delkey = lru; + prealloc = 0; + goto done; + } + /* check that we aren't going to overrun the user's quota */ ret = key_payload_reserve(keyring, keyring->datalen + KEYQUOTA_LINK_BYTES); @@ -786,15 +818,14 @@ int __key_link_begin(struct key *keyring, const struct key_type *type, } else { /* grow the key list */ max = 4; - if (klist) + if (klist) { max += klist->maxkeys; + if (max > MAX_KEYRING_LINKS) + max = MAX_KEYRING_LINKS; + BUG_ON(max <= klist->maxkeys); + } - ret = -ENFILE; - if (max > USHRT_MAX - 1) - goto error_quota; size = sizeof(*klist) + sizeof(struct key *) * max; - if (size > PAGE_SIZE) - goto error_quota; ret = -ENOMEM; nklist = kmalloc(size, GFP_KERNEL); @@ -873,6 +904,8 @@ void __key_link(struct key *keyring, struct key *key, klist = rcu_dereference_locked_keyring(keyring); atomic_inc(&key->usage); + keyring->last_used_at = key->last_used_at = + current_kernel_time().tv_sec; /* there's a matching key we can displace or an empty slot in a newly * allocated list we can fill */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index be7ecb2018dd..e137fcd7042c 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -732,6 +732,8 @@ try_again: if (ret < 0) goto invalid_key; + key->last_used_at = current_kernel_time().tv_sec; + error: put_cred(cred); return key_ref; -- cgit v1.2.2 From fd75815f727f157a05f4c96b5294a4617c0557da Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 May 2012 10:56:56 +0100 Subject: KEYS: Add invalidation support Add support for invalidating a key - which renders it immediately invisible to further searches and causes the garbage collector to immediately wake up, remove it from keyrings and then destroy it when it's no longer referenced. It's better not to do this with keyctl_revoke() as that marks the key to start returning -EKEYREVOKED to searches when what is actually desired is to have the key refetched. To invalidate a key the caller must be granted SEARCH permission by the key. This may be too strict. It may be better to also permit invalidation if the caller has any of READ, WRITE or SETATTR permission. The primary use for this is to evict keys that are cached in special keyrings, such as the DNS resolver or an ID mapper. Signed-off-by: David Howells --- security/keys/compat.c | 3 +++ security/keys/gc.c | 21 ++++++++++++++------- security/keys/internal.h | 15 ++++++++++++++- security/keys/key.c | 22 ++++++++++++++++++++++ security/keys/keyctl.c | 34 ++++++++++++++++++++++++++++++++++ security/keys/keyring.c | 25 +++++++++++-------------- security/keys/permission.c | 15 ++++++++++----- security/keys/proc.c | 3 ++- 8 files changed, 110 insertions(+), 28 deletions(-) (limited to 'security') diff --git a/security/keys/compat.c b/security/keys/compat.c index 4c48e13448f8..fab4f8dda6c6 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -135,6 +135,9 @@ asmlinkage long compat_sys_keyctl(u32 option, return compat_keyctl_instantiate_key_iov( arg2, compat_ptr(arg3), arg4, arg5); + case KEYCTL_INVALIDATE: + return keyctl_invalidate_key(arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/gc.c b/security/keys/gc.c index adddaa258d50..61ab7c82ebb1 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -71,6 +71,15 @@ void key_schedule_gc(time_t gc_at) } } +/* + * Schedule a dead links collection run. + */ +void key_schedule_gc_links(void) +{ + set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); + queue_work(system_nrt_wq, &key_gc_work); +} + /* * Some key's cleanup time was met after it expired, so we need to get the * reaper to go through a cycle finding expired keys. @@ -79,8 +88,7 @@ static void key_gc_timer_func(unsigned long data) { kenter(""); key_gc_next_run = LONG_MAX; - set_bit(KEY_GC_KEY_EXPIRED, &key_gc_flags); - queue_work(system_nrt_wq, &key_gc_work); + key_schedule_gc_links(); } /* @@ -131,12 +139,12 @@ void key_gc_keytype(struct key_type *ktype) static void key_gc_keyring(struct key *keyring, time_t limit) { struct keyring_list *klist; - struct key *key; int loop; kenter("%x", key_serial(keyring)); - if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) + if (keyring->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) goto dont_gc; /* scan the keyring looking for dead keys */ @@ -148,9 +156,8 @@ static void key_gc_keyring(struct key *keyring, time_t limit) loop = klist->nkeys; smp_rmb(); for (loop--; loop >= 0; loop--) { - key = rcu_dereference(klist->keys[loop]); - if (test_bit(KEY_FLAG_DEAD, &key->flags) || - (key->expiry > 0 && key->expiry <= limit)) + struct key *key = rcu_dereference(klist->keys[loop]); + if (key_is_dead(key, limit)) goto do_gc; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 65647f825584..f711b094ed41 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -152,7 +152,8 @@ extern long join_session_keyring(const char *name); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); -extern void key_schedule_gc(time_t expiry_at); +extern void key_schedule_gc(time_t gc_at); +extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); extern int key_task_permission(const key_ref_t key_ref, @@ -196,6 +197,17 @@ extern struct key *request_key_auth_new(struct key *target, extern struct key *key_get_instantiation_authkey(key_serial_t target_id); +/* + * Determine whether a key is dead. + */ +static inline bool key_is_dead(struct key *key, time_t limit) +{ + return + key->flags & ((1 << KEY_FLAG_DEAD) | + (1 << KEY_FLAG_INVALIDATED)) || + (key->expiry > 0 && key->expiry <= limit); +} + /* * keyctl() functions */ @@ -225,6 +237,7 @@ extern long keyctl_reject_key(key_serial_t, unsigned, unsigned, key_serial_t); extern long keyctl_instantiate_key_iov(key_serial_t, const struct iovec __user *, unsigned, key_serial_t); +extern long keyctl_invalidate_key(key_serial_t); extern long keyctl_instantiate_key_common(key_serial_t, const struct iovec __user *, diff --git a/security/keys/key.c b/security/keys/key.c index dc628941ecd3..c9bf66ac36e0 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -954,6 +954,28 @@ void key_revoke(struct key *key) } EXPORT_SYMBOL(key_revoke); +/** + * key_invalidate - Invalidate a key. + * @key: The key to be invalidated. + * + * Mark a key as being invalidated and have it cleaned up immediately. The key + * is ignored by all searches and other operations from this point. + */ +void key_invalidate(struct key *key) +{ + kenter("%d", key_serial(key)); + + key_check(key); + + if (!test_bit(KEY_FLAG_INVALIDATED, &key->flags)) { + down_write_nested(&key->sem, 1); + if (!test_and_set_bit(KEY_FLAG_INVALIDATED, &key->flags)) + key_schedule_gc_links(); + up_write(&key->sem); + } +} +EXPORT_SYMBOL(key_invalidate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index fb767c6cd99f..ddb3e05bc5fc 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -374,6 +374,37 @@ error: return ret; } +/* + * Invalidate a key. + * + * The key must be grant the caller Invalidate permission for this to work. + * The key and any links to the key will be automatically garbage collected + * immediately. + * + * If successful, 0 is returned. + */ +long keyctl_invalidate_key(key_serial_t id) +{ + key_ref_t key_ref; + long ret; + + kenter("%d", id); + + key_ref = lookup_user_key(id, 0, KEY_SEARCH); + if (IS_ERR(key_ref)) { + ret = PTR_ERR(key_ref); + goto error; + } + + key_invalidate(key_ref_to_ptr(key_ref)); + ret = 0; + + key_ref_put(key_ref); +error: + kleave(" = %ld", ret); + return ret; +} + /* * Clear the specified keyring, creating an empty process keyring if one of the * special keyring IDs is used. @@ -1622,6 +1653,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (unsigned) arg4, (key_serial_t) arg5); + case KEYCTL_INVALIDATE: + return keyctl_invalidate_key((key_serial_t) arg2); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 89d02cfb00c2..7445875f6818 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -382,13 +382,17 @@ key_ref_t keyring_search_aux(key_ref_t keyring_ref, /* otherwise, the top keyring must not be revoked, expired, or * negatively instantiated if we are to search it */ key_ref = ERR_PTR(-EAGAIN); - if (kflags & ((1 << KEY_FLAG_REVOKED) | (1 << KEY_FLAG_NEGATIVE)) || + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_NEGATIVE)) || (keyring->expiry && now.tv_sec >= keyring->expiry)) goto error_2; /* start processing a new keyring */ descend: - if (test_bit(KEY_FLAG_REVOKED, &keyring->flags)) + kflags = keyring->flags; + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) goto not_this_keyring; keylist = rcu_dereference(keyring->payload.subscriptions); @@ -406,9 +410,10 @@ descend: if (key->type != type) continue; - /* skip revoked keys and expired keys */ + /* skip invalidated, revoked and expired keys */ if (!no_state_check) { - if (kflags & (1 << KEY_FLAG_REVOKED)) + if (kflags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) continue; if (key->expiry && now.tv_sec >= key->expiry) @@ -559,7 +564,8 @@ key_ref_t __keyring_search_one(key_ref_t keyring_ref, key->type->match(key, description)) && key_permission(make_key_ref(key, possessed), perm) == 0 && - !test_bit(KEY_FLAG_REVOKED, &key->flags) + !(key->flags & ((1 << KEY_FLAG_INVALIDATED) | + (1 << KEY_FLAG_REVOKED))) ) goto found; } @@ -1176,15 +1182,6 @@ static void keyring_revoke(struct key *keyring) } } -/* - * Determine whether a key is dead. - */ -static bool key_is_dead(struct key *key, time_t limit) -{ - return test_bit(KEY_FLAG_DEAD, &key->flags) || - (key->expiry > 0 && key->expiry <= limit); -} - /* * Collect garbage from the contents of a keyring, replacing the old list with * a new one with the pointers all shuffled down. diff --git a/security/keys/permission.c b/security/keys/permission.c index c35b5229e3cd..5f4c00c0947d 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -87,20 +87,25 @@ EXPORT_SYMBOL(key_task_permission); * key_validate - Validate a key. * @key: The key to be validated. * - * Check that a key is valid, returning 0 if the key is okay, -EKEYREVOKED if - * the key's type has been removed or if the key has been revoked or - * -EKEYEXPIRED if the key has expired. + * Check that a key is valid, returning 0 if the key is okay, -ENOKEY if the + * key is invalidated, -EKEYREVOKED if the key's type has been removed or if + * the key has been revoked or -EKEYEXPIRED if the key has expired. */ int key_validate(struct key *key) { struct timespec now; + unsigned long flags = key->flags; int ret = 0; if (key) { + ret = -ENOKEY; + if (flags & (1 << KEY_FLAG_INVALIDATED)) + goto error; + /* check it's still accessible */ ret = -EKEYREVOKED; - if (test_bit(KEY_FLAG_REVOKED, &key->flags) || - test_bit(KEY_FLAG_DEAD, &key->flags)) + if (flags & ((1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))) goto error; /* check it hasn't expired */ diff --git a/security/keys/proc.c b/security/keys/proc.c index 49bbc97943ad..30d1ddfd9cef 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -242,7 +242,7 @@ static int proc_keys_show(struct seq_file *m, void *v) #define showflag(KEY, LETTER, FLAG) \ (test_bit(FLAG, &(KEY)->flags) ? LETTER : '-') - seq_printf(m, "%08x %c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", + seq_printf(m, "%08x %c%c%c%c%c%c%c %5d %4s %08x %5d %5d %-9.9s ", key->serial, showflag(key, 'I', KEY_FLAG_INSTANTIATED), showflag(key, 'R', KEY_FLAG_REVOKED), @@ -250,6 +250,7 @@ static int proc_keys_show(struct seq_file *m, void *v) showflag(key, 'Q', KEY_FLAG_IN_QUOTA), showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), showflag(key, 'N', KEY_FLAG_NEGATIVE), + showflag(key, 'i', KEY_FLAG_INVALIDATED), atomic_read(&key->usage), xbuf, key->perm, -- cgit v1.2.2 From 77b513dda90fd99bd1225410b25e745b74779c1c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 13 May 2012 23:03:23 +0900 Subject: TOMOYO: Accept manager programs which do not start with / . The pathname of /usr/sbin/tomoyo-editpolicy seen from Ubuntu 12.04 Live CD is squashfs:/usr/sbin/tomoyo-editpolicy rather than /usr/sbin/tomoyo-editpolicy . Therefore, we need to accept manager programs which do not start with / . Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 26 ++++++-------------------- security/tomoyo/common.h | 1 - 2 files changed, 6 insertions(+), 21 deletions(-) (limited to 'security') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 8656b16eef7b..2e0f12c62938 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -850,14 +850,9 @@ static int tomoyo_update_manager_entry(const char *manager, policy_list[TOMOYO_ID_MANAGER], }; int error = is_delete ? -ENOENT : -ENOMEM; - if (tomoyo_domain_def(manager)) { - if (!tomoyo_correct_domain(manager)) - return -EINVAL; - e.is_domain = true; - } else { - if (!tomoyo_correct_path(manager)) - return -EINVAL; - } + if (!tomoyo_correct_domain(manager) && + !tomoyo_correct_word(manager)) + return -EINVAL; e.manager = tomoyo_get_name(manager); if (e.manager) { error = tomoyo_update_policy(&e.head, sizeof(e), ¶m, @@ -932,23 +927,14 @@ static bool tomoyo_manager(void) return true; if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) return false; - list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER], head.list) { - if (!ptr->head.is_deleted && ptr->is_domain - && !tomoyo_pathcmp(domainname, ptr->manager)) { - found = true; - break; - } - } - if (found) - return true; exe = tomoyo_get_exe(); if (!exe) return false; list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. policy_list[TOMOYO_ID_MANAGER], head.list) { - if (!ptr->head.is_deleted && !ptr->is_domain - && !strcmp(exe, ptr->manager->name)) { + if (!ptr->head.is_deleted && + (!tomoyo_pathcmp(domainname, ptr->manager) || + !strcmp(exe, ptr->manager->name))) { found = true; break; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 30fd98369700..75e4dc1c02a0 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -860,7 +860,6 @@ struct tomoyo_aggregator { /* Structure for policy manager. */ struct tomoyo_manager { struct tomoyo_acl_head head; - bool is_domain; /* True if manager is a domainname. */ /* A path to program or a domainname. */ const struct tomoyo_path_info *manager; }; -- cgit v1.2.2 From 2cc8a71641b4460783ea3bd7a3476043fdf85397 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 14 May 2012 10:19:28 -0700 Subject: Yama: replace capable() with ns_capable() When checking capabilities, the question we want to be asking is "does current() have the capability in the child's namespace?" Signed-off-by: Kees Cook Signed-off-by: James Morris --- security/yama/yama_lsm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index c852f7472ad0..83554ee8a587 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -264,11 +264,11 @@ static int yama_ptrace_access_check(struct task_struct *child, case YAMA_SCOPE_RELATIONAL: if (!task_is_descendant(current, child) && !ptracer_exception_found(current, child) && - !capable(CAP_SYS_PTRACE)) + !ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) rc = -EPERM; break; case YAMA_SCOPE_CAPABILITY: - if (!capable(CAP_SYS_PTRACE)) + if (!ns_capable(task_user_ns(child), CAP_SYS_PTRACE)) rc = -EPERM; break; case YAMA_SCOPE_NO_ATTACH: -- cgit v1.2.2 From 2267b13a7cad1f9dfe0073c1f902d45953f9faff Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Tue, 13 Mar 2012 19:14:19 -0700 Subject: Smack: recursive tramsmute The transmuting directory feature of Smack requires that the transmuting attribute be explicitly set in all cases. It seems the users of this facility would expect that the transmuting attribute be inherited by subdirectories that are created in a transmuting directory. This does not seem to add any additional complexity to the understanding of how the system works. Signed-off-by: Casey Schaufler --- security/smack/smack.h | 1 + security/smack/smack_lsm.c | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index 4ede719922ed..cf2594dfa933 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -66,6 +66,7 @@ struct task_smack { #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ #define SMK_INODE_TRANSMUTE 0x02 /* directory is transmuting */ +#define SMK_INODE_CHANGED 0x04 /* smack was transmuted */ /* * A label access rule. diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 45c32f074166..df45d3939188 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -556,6 +556,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, void **value, size_t *len) { struct smack_known *skp; + struct inode_smack *issp = inode->i_security; char *csp = smk_of_current(); char *isp = smk_of_inode(inode); char *dsp = smk_of_inode(dir); @@ -577,10 +578,13 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, * If the access rule allows transmutation and * the directory requests transmutation then * by all means transmute. + * Mark the inode as changed. */ if (may > 0 && ((may & MAY_TRANSMUTE) != 0) && - smk_inode_transmutable(dir)) + smk_inode_transmutable(dir)) { isp = dsp; + issp->smk_flags |= SMK_INODE_CHANGED; + } *value = kstrdup(isp, GFP_KERNEL); if (*value == NULL) @@ -2552,6 +2556,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) char *final; char trattr[TRANS_TRUE_SIZE]; int transflag = 0; + int rc; struct dentry *dp; if (inode == NULL) @@ -2670,17 +2675,38 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ dp = dget(opt_dentry); fetched = smk_fetch(XATTR_NAME_SMACK, inode, dp); - if (fetched != NULL) { + if (fetched != NULL) final = fetched; - if (S_ISDIR(inode->i_mode)) { - trattr[0] = '\0'; - inode->i_op->getxattr(dp, + + /* + * Transmuting directory + */ + if (S_ISDIR(inode->i_mode)) { + /* + * If this is a new directory and the label was + * transmuted when the inode was initialized + * set the transmute attribute on the directory + * and mark the inode. + * + * If there is a transmute attribute on the + * directory mark the inode. + */ + if (isp->smk_flags & SMK_INODE_CHANGED) { + isp->smk_flags &= ~SMK_INODE_CHANGED; + rc = inode->i_op->setxattr(dp, XATTR_NAME_SMACKTRANSMUTE, - trattr, TRANS_TRUE_SIZE); - if (strncmp(trattr, TRANS_TRUE, - TRANS_TRUE_SIZE) == 0) - transflag = SMK_INODE_TRANSMUTE; + TRANS_TRUE, TRANS_TRUE_SIZE, + 0); + } else { + rc = inode->i_op->getxattr(dp, + XATTR_NAME_SMACKTRANSMUTE, trattr, + TRANS_TRUE_SIZE); + if (rc >= 0 && strncmp(trattr, TRANS_TRUE, + TRANS_TRUE_SIZE) != 0) + rc = -EINVAL; } + if (rc >= 0) + transflag = SMK_INODE_TRANSMUTE; } isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); -- cgit v1.2.2 From ceffec5541cc22486d3ff492e3d76a33a68fbfa3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 29 Mar 2012 16:19:05 +0900 Subject: gfp flags for security_inode_alloc()? Dave Chinner wrote: > Yes, because you have no idea what the calling context is except > for the fact that is from somewhere inside filesystem code and the > filesystem could be holding locks. Therefore, GFP_NOFS is really the > only really safe way to allocate memory here. I see. Thank you. I'm not sure, but can call trace happen where somewhere inside network filesystem or stackable filesystem code with locks held invokes operations that involves GFP_KENREL memory allocation outside that filesystem? ---------- [PATCH] SMACK: Fix incorrect GFP_KERNEL usage. new_inode_smack() which can be called from smack_inode_alloc_security() needs to use GFP_NOFS like SELinux's inode_alloc_security() does, for security_inode_alloc() is called from inode_init_always() and inode_init_always() is called from xfs_inode_alloc() which is using GFP_NOFS. smack_inode_init_security() needs to use GFP_NOFS like selinux_inode_init_security() does, for initxattrs() callback function (e.g. btrfs_initxattrs()) which is called from security_inode_init_security() is using GFP_NOFS. smack_audit_rule_match() needs to use GFP_ATOMIC, for security_audit_rule_match() can be called from audit_filter_user_rules() and audit_filter_user_rules() is called from audit_filter_user() with RCU read lock held. Signed-off-by: Tetsuo Handa Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index df45d3939188..5f80075d7713 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -79,7 +79,7 @@ struct inode_smack *new_inode_smack(char *smack) { struct inode_smack *isp; - isp = kzalloc(sizeof(struct inode_smack), GFP_KERNEL); + isp = kzalloc(sizeof(struct inode_smack), GFP_NOFS); if (isp == NULL) return NULL; @@ -563,7 +563,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, int may; if (name) { - *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_KERNEL); + *name = kstrdup(XATTR_SMACK_SUFFIX, GFP_NOFS); if (*name == NULL) return -ENOMEM; } @@ -586,7 +586,7 @@ static int smack_inode_init_security(struct inode *inode, struct inode *dir, issp->smk_flags |= SMK_INODE_CHANGED; } - *value = kstrdup(isp, GFP_KERNEL); + *value = kstrdup(isp, GFP_NOFS); if (*value == NULL) return -ENOMEM; } @@ -3426,7 +3426,7 @@ static int smack_audit_rule_match(u32 secid, u32 field, u32 op, void *vrule, char *rule = vrule; if (!rule) { - audit_log(actx, GFP_KERNEL, AUDIT_SELINUX_ERR, + audit_log(actx, GFP_ATOMIC, AUDIT_SELINUX_ERR, "Smack: missing rule\n"); return -ENOENT; } -- cgit v1.2.2 From f7112e6c9abf1c70f001dcf097c1d6e218a93f5c Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Sun, 6 May 2012 15:22:02 -0700 Subject: Smack: allow for significantly longer Smack labels v4 V4 updated to current linux-security#next Targeted for git://gitorious.org/smack-next/kernel.git Modern application runtime environments like to use naming schemes that are structured and generated without human intervention. Even though the Smack limit of 23 characters for a label name is perfectly rational for human use there have been complaints that the limit is a problem in environments where names are composed from a set or sources, including vendor, author, distribution channel and application name. Names like softwarehouse-pgwodehouse-coolappstore-mellowmuskrats are becoming harder to avoid. This patch introduces long label support in Smack. Labels are now limited to 255 characters instead of the old 23. The primary reason for limiting the labels to 23 characters was so they could be directly contained in CIPSO category sets. This is still done were possible, but for labels that are too large a mapping is required. This is perfectly safe for communication that stays "on the box" and doesn't require much coordination between boxes beyond what would have been required to keep label names consistent. The bulk of this patch is in smackfs, adding and updating administrative interfaces. Because existing APIs can't be changed new ones that do much the same things as old ones have been introduced. The Smack specific CIPSO data representation has been removed and replaced with the data format used by netlabel. The CIPSO header is now computed when a label is imported rather than on use. This results in improved IP performance. The smack label is now allocated separately from the containing structure, allowing for larger strings. Four new /smack interfaces have been introduced as four of the old interfaces strictly required labels be specified in fixed length arrays. The access interface is supplemented with the check interface: access "Subject Object rwxat" access2 "Subject Object rwaxt" The load interface is supplemented with the rules interface: load "Subject Object rwxat" load2 "Subject Object rwaxt" The load-self interface is supplemented with the self-rules interface: load-self "Subject Object rwxat" load-self2 "Subject Object rwaxt" The cipso interface is supplemented with the wire interface: cipso "Subject lvl cnt c1 c2 ..." cipso2 "Subject lvl cnt c1 c2 ..." The old interfaces are maintained for compatibility. Signed-off-by: Casey Schaufler --- security/smack/smack.h | 56 +-- security/smack/smack_access.c | 233 +++++----- security/smack/smack_lsm.c | 185 ++------ security/smack/smackfs.c | 993 ++++++++++++++++++++++++++++++++---------- 4 files changed, 941 insertions(+), 526 deletions(-) (limited to 'security') diff --git a/security/smack/smack.h b/security/smack/smack.h index cf2594dfa933..5e031a2e4c36 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -23,13 +23,19 @@ #include /* + * Smack labels were limited to 23 characters for a long time. + */ +#define SMK_LABELLEN 24 +#define SMK_LONGLABEL 256 + +/* + * Maximum number of bytes for the levels in a CIPSO IP option. * Why 23? CIPSO is constrained to 30, so a 32 byte buffer is * bigger than can be used, and 24 is the next lower multiple * of 8, and there are too many issues if there isn't space set * aside for the terminating null byte. */ -#define SMK_MAXLEN 23 -#define SMK_LABELLEN (SMK_MAXLEN+1) +#define SMK_CIPSOLEN 24 struct superblock_smack { char *smk_root; @@ -78,15 +84,6 @@ struct smack_rule { int smk_access; }; -/* - * An entry in the table mapping smack values to - * CIPSO level/category-set values. - */ -struct smack_cipso { - int smk_level; - char smk_catset[SMK_LABELLEN]; -}; - /* * An entry in the table identifying hosts. */ @@ -114,22 +111,19 @@ struct smk_netlbladdr { * interfaces don't. The secid should go away when all of * these components have been repaired. * - * If there is a cipso value associated with the label it - * gets stored here, too. This will most likely be rare as - * the cipso direct mapping in used internally. + * The cipso value associated with the label gets stored here, too. * * Keep the access rules for this subject label here so that * the entire set of rules does not need to be examined every * time. */ struct smack_known { - struct list_head list; - char smk_known[SMK_LABELLEN]; - u32 smk_secid; - struct smack_cipso *smk_cipso; - spinlock_t smk_cipsolock; /* for changing cipso map */ - struct list_head smk_rules; /* access rules */ - struct mutex smk_rules_lock; /* lock for the rules */ + struct list_head list; + char *smk_known; + u32 smk_secid; + struct netlbl_lsm_secattr smk_netlabel; /* on wire labels */ + struct list_head smk_rules; /* access rules */ + struct mutex smk_rules_lock; /* lock for rules */ }; /* @@ -166,6 +160,7 @@ struct smack_known { #define SMACK_CIPSO_DOI_DEFAULT 3 /* Historical */ #define SMACK_CIPSO_DOI_INVALID -1 /* Not a DOI */ #define SMACK_CIPSO_DIRECT_DEFAULT 250 /* Arbitrary */ +#define SMACK_CIPSO_MAPPED_DEFAULT 251 /* Also arbitrary */ #define SMACK_CIPSO_MAXCATVAL 63 /* Bigger gets harder */ #define SMACK_CIPSO_MAXLEVEL 255 /* CIPSO 2.2 standard */ #define SMACK_CIPSO_MAXCATNUM 239 /* CIPSO 2.2 standard */ @@ -216,10 +211,9 @@ struct inode_smack *new_inode_smack(char *); int smk_access_entry(char *, char *, struct list_head *); int smk_access(char *, char *, int, struct smk_audit_info *); int smk_curacc(char *, u32, struct smk_audit_info *); -int smack_to_cipso(const char *, struct smack_cipso *); -char *smack_from_cipso(u32, char *); char *smack_from_secid(const u32); -void smk_parse_smack(const char *string, int len, char *smack); +char *smk_parse_smack(const char *string, int len); +int smk_netlbl_mls(int, char *, struct netlbl_lsm_secattr *, int); char *smk_import(const char *, int); struct smack_known *smk_import_entry(const char *, int); struct smack_known *smk_find_entry(const char *); @@ -229,6 +223,7 @@ u32 smack_to_secid(const char *); * Shared data. */ extern int smack_cipso_direct; +extern int smack_cipso_mapped; extern char *smack_net_ambient; extern char *smack_onlycap; extern const char *smack_cipso_option; @@ -240,23 +235,12 @@ extern struct smack_known smack_known_invalid; extern struct smack_known smack_known_star; extern struct smack_known smack_known_web; +extern struct mutex smack_known_lock; extern struct list_head smack_known_list; extern struct list_head smk_netlbladdr_list; extern struct security_operations smack_ops; -/* - * Stricly for CIPSO level manipulation. - * Set the category bit number in a smack label sized buffer. - */ -static inline void smack_catset_bit(int cat, char *catsetp) -{ - if (cat > SMK_LABELLEN * 8) - return; - - catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); -} - /* * Is the directory transmuting? */ diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index c8115f7308f8..9f3705e92712 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -19,37 +19,31 @@ struct smack_known smack_known_huh = { .smk_known = "?", .smk_secid = 2, - .smk_cipso = NULL, }; struct smack_known smack_known_hat = { .smk_known = "^", .smk_secid = 3, - .smk_cipso = NULL, }; struct smack_known smack_known_star = { .smk_known = "*", .smk_secid = 4, - .smk_cipso = NULL, }; struct smack_known smack_known_floor = { .smk_known = "_", .smk_secid = 5, - .smk_cipso = NULL, }; struct smack_known smack_known_invalid = { .smk_known = "", .smk_secid = 6, - .smk_cipso = NULL, }; struct smack_known smack_known_web = { .smk_known = "@", .smk_secid = 7, - .smk_cipso = NULL, }; LIST_HEAD(smack_known_list); @@ -331,7 +325,7 @@ void smack_log(char *subject_label, char *object_label, int request, } #endif -static DEFINE_MUTEX(smack_known_lock); +DEFINE_MUTEX(smack_known_lock); /** * smk_find_entry - find a label on the list, return the list entry @@ -345,7 +339,7 @@ struct smack_known *smk_find_entry(const char *string) struct smack_known *skp; list_for_each_entry_rcu(skp, &smack_known_list, list) { - if (strncmp(skp->smk_known, string, SMK_MAXLEN) == 0) + if (strcmp(skp->smk_known, string) == 0) return skp; } @@ -356,27 +350,76 @@ struct smack_known *smk_find_entry(const char *string) * smk_parse_smack - parse smack label from a text string * @string: a text string that might contain a Smack label * @len: the maximum size, or zero if it is NULL terminated. - * @smack: parsed smack label, or NULL if parse error + * + * Returns a pointer to the clean label, or NULL */ -void smk_parse_smack(const char *string, int len, char *smack) +char *smk_parse_smack(const char *string, int len) { - int found; + char *smack; int i; - if (len <= 0 || len > SMK_MAXLEN) - len = SMK_MAXLEN; - - for (i = 0, found = 0; i < SMK_LABELLEN; i++) { - if (found) - smack[i] = '\0'; - else if (i >= len || string[i] > '~' || string[i] <= ' ' || - string[i] == '/' || string[i] == '"' || - string[i] == '\\' || string[i] == '\'') { - smack[i] = '\0'; - found = 1; - } else - smack[i] = string[i]; + if (len <= 0) + len = strlen(string) + 1; + + /* + * Reserve a leading '-' as an indicator that + * this isn't a label, but an option to interfaces + * including /smack/cipso and /smack/cipso2 + */ + if (string[0] == '-') + return NULL; + + for (i = 0; i < len; i++) + if (string[i] > '~' || string[i] <= ' ' || string[i] == '/' || + string[i] == '"' || string[i] == '\\' || string[i] == '\'') + break; + + if (i == 0 || i >= SMK_LONGLABEL) + return NULL; + + smack = kzalloc(i + 1, GFP_KERNEL); + if (smack != NULL) { + strncpy(smack, string, i + 1); + smack[i] = '\0'; } + return smack; +} + +/** + * smk_netlbl_mls - convert a catset to netlabel mls categories + * @catset: the Smack categories + * @sap: where to put the netlabel categories + * + * Allocates and fills attr.mls + * Returns 0 on success, error code on failure. + */ +int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, + int len) +{ + unsigned char *cp; + unsigned char m; + int cat; + int rc; + int byte; + + sap->flags |= NETLBL_SECATTR_MLS_CAT; + sap->attr.mls.lvl = level; + sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); + sap->attr.mls.cat->startbit = 0; + + for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) + for (m = 0x80; m != 0; m >>= 1, cat++) { + if ((m & *cp) == 0) + continue; + rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat, + cat, GFP_ATOMIC); + if (rc < 0) { + netlbl_secattr_catmap_free(sap->attr.mls.cat); + return rc; + } + } + + return 0; } /** @@ -390,33 +433,59 @@ void smk_parse_smack(const char *string, int len, char *smack) struct smack_known *smk_import_entry(const char *string, int len) { struct smack_known *skp; - char smack[SMK_LABELLEN]; + char *smack; + int slen; + int rc; - smk_parse_smack(string, len, smack); - if (smack[0] == '\0') + smack = smk_parse_smack(string, len); + if (smack == NULL) return NULL; mutex_lock(&smack_known_lock); skp = smk_find_entry(smack); + if (skp != NULL) + goto freeout; - if (skp == NULL) { - skp = kzalloc(sizeof(struct smack_known), GFP_KERNEL); - if (skp != NULL) { - strncpy(skp->smk_known, smack, SMK_MAXLEN); - skp->smk_secid = smack_next_secid++; - skp->smk_cipso = NULL; - INIT_LIST_HEAD(&skp->smk_rules); - spin_lock_init(&skp->smk_cipsolock); - mutex_init(&skp->smk_rules_lock); - /* - * Make sure that the entry is actually - * filled before putting it on the list. - */ - list_add_rcu(&skp->list, &smack_known_list); - } - } + skp = kzalloc(sizeof(*skp), GFP_KERNEL); + if (skp == NULL) + goto freeout; + skp->smk_known = smack; + skp->smk_secid = smack_next_secid++; + skp->smk_netlabel.domain = skp->smk_known; + skp->smk_netlabel.flags = + NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; + /* + * If direct labeling works use it. + * Otherwise use mapped labeling. + */ + slen = strlen(smack); + if (slen < SMK_CIPSOLEN) + rc = smk_netlbl_mls(smack_cipso_direct, skp->smk_known, + &skp->smk_netlabel, slen); + else + rc = smk_netlbl_mls(smack_cipso_mapped, (char *)&skp->smk_secid, + &skp->smk_netlabel, sizeof(skp->smk_secid)); + + if (rc >= 0) { + INIT_LIST_HEAD(&skp->smk_rules); + mutex_init(&skp->smk_rules_lock); + /* + * Make sure that the entry is actually + * filled before putting it on the list. + */ + list_add_rcu(&skp->list, &smack_known_list); + goto unlockout; + } + /* + * smk_netlbl_mls failed. + */ + kfree(skp); + skp = NULL; +freeout: + kfree(smack); +unlockout: mutex_unlock(&smack_known_lock); return skp; @@ -479,79 +548,9 @@ char *smack_from_secid(const u32 secid) */ u32 smack_to_secid(const char *smack) { - struct smack_known *skp; + struct smack_known *skp = smk_find_entry(smack); - rcu_read_lock(); - list_for_each_entry_rcu(skp, &smack_known_list, list) { - if (strncmp(skp->smk_known, smack, SMK_MAXLEN) == 0) { - rcu_read_unlock(); - return skp->smk_secid; - } - } - rcu_read_unlock(); - return 0; -} - -/** - * smack_from_cipso - find the Smack label associated with a CIPSO option - * @level: Bell & LaPadula level from the network - * @cp: Bell & LaPadula categories from the network - * - * This is a simple lookup in the label table. - * - * Return the matching label from the label list or NULL. - */ -char *smack_from_cipso(u32 level, char *cp) -{ - struct smack_known *kp; - char *final = NULL; - - rcu_read_lock(); - list_for_each_entry(kp, &smack_known_list, list) { - if (kp->smk_cipso == NULL) - continue; - - spin_lock_bh(&kp->smk_cipsolock); - - if (kp->smk_cipso->smk_level == level && - memcmp(kp->smk_cipso->smk_catset, cp, SMK_LABELLEN) == 0) - final = kp->smk_known; - - spin_unlock_bh(&kp->smk_cipsolock); - - if (final != NULL) - break; - } - rcu_read_unlock(); - - return final; -} - -/** - * smack_to_cipso - find the CIPSO option to go with a Smack label - * @smack: a pointer to the smack label in question - * @cp: where to put the result - * - * Returns zero if a value is available, non-zero otherwise. - */ -int smack_to_cipso(const char *smack, struct smack_cipso *cp) -{ - struct smack_known *kp; - int found = 0; - - rcu_read_lock(); - list_for_each_entry_rcu(kp, &smack_known_list, list) { - if (kp->smk_known == smack || - strcmp(kp->smk_known, smack) == 0) { - found = 1; - break; - } - } - rcu_read_unlock(); - - if (found == 0 || kp->smk_cipso == NULL) - return -ENOENT; - - memcpy(cp, kp->smk_cipso, sizeof(struct smack_cipso)); - return 0; + if (skp == NULL) + return 0; + return skp->smk_secid; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 5f80075d7713..952b1f41fc78 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -57,16 +56,23 @@ static char *smk_fetch(const char *name, struct inode *ip, struct dentry *dp) { int rc; - char in[SMK_LABELLEN]; + char *buffer; + char *result = NULL; if (ip->i_op->getxattr == NULL) return NULL; - rc = ip->i_op->getxattr(dp, name, in, SMK_LABELLEN); - if (rc < 0) + buffer = kzalloc(SMK_LONGLABEL, GFP_KERNEL); + if (buffer == NULL) return NULL; - return smk_import(in, rc); + rc = ip->i_op->getxattr(dp, name, buffer, SMK_LONGLABEL); + if (rc > 0) + result = smk_import(buffer, rc); + + kfree(buffer); + + return result; } /** @@ -825,7 +831,7 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, * check label validity here so import wont fail on * post_setxattr */ - if (size == 0 || size >= SMK_LABELLEN || + if (size == 0 || size >= SMK_LONGLABEL || smk_import(value, size) == NULL) rc = -EINVAL; } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { @@ -1823,65 +1829,6 @@ static char *smack_host_label(struct sockaddr_in *sip) return NULL; } -/** - * smack_set_catset - convert a capset to netlabel mls categories - * @catset: the Smack categories - * @sap: where to put the netlabel categories - * - * Allocates and fills attr.mls.cat - */ -static void smack_set_catset(char *catset, struct netlbl_lsm_secattr *sap) -{ - unsigned char *cp; - unsigned char m; - int cat; - int rc; - int byte; - - if (!catset) - return; - - sap->flags |= NETLBL_SECATTR_MLS_CAT; - sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - sap->attr.mls.cat->startbit = 0; - - for (cat = 1, cp = catset, byte = 0; byte < SMK_LABELLEN; cp++, byte++) - for (m = 0x80; m != 0; m >>= 1, cat++) { - if ((m & *cp) == 0) - continue; - rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat, - cat, GFP_ATOMIC); - } -} - -/** - * smack_to_secattr - fill a secattr from a smack value - * @smack: the smack value - * @nlsp: where the result goes - * - * Casey says that CIPSO is good enough for now. - * It can be used to effect. - * It can also be abused to effect when necessary. - * Apologies to the TSIG group in general and GW in particular. - */ -static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) -{ - struct smack_cipso cipso; - int rc; - - nlsp->domain = smack; - nlsp->flags = NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; - - rc = smack_to_cipso(smack, &cipso); - if (rc == 0) { - nlsp->attr.mls.lvl = cipso.smk_level; - smack_set_catset(cipso.smk_catset, nlsp); - } else { - nlsp->attr.mls.lvl = smack_cipso_direct; - smack_set_catset(smack, nlsp); - } -} - /** * smack_netlabel - Set the secattr on a socket * @sk: the socket @@ -1894,8 +1841,8 @@ static void smack_to_secattr(char *smack, struct netlbl_lsm_secattr *nlsp) */ static int smack_netlabel(struct sock *sk, int labeled) { + struct smack_known *skp; struct socket_smack *ssp = sk->sk_security; - struct netlbl_lsm_secattr secattr; int rc = 0; /* @@ -1913,10 +1860,8 @@ static int smack_netlabel(struct sock *sk, int labeled) labeled == SMACK_UNLABELED_SOCKET) netlbl_sock_delattr(sk); else { - netlbl_secattr_init(&secattr); - smack_to_secattr(ssp->smk_out, &secattr); - rc = netlbl_sock_setattr(sk, sk->sk_family, &secattr); - netlbl_secattr_destroy(&secattr); + skp = smk_find_entry(ssp->smk_out); + rc = netlbl_sock_setattr(sk, sk->sk_family, &skp->smk_netlabel); } bh_unlock_sock(sk); @@ -1989,7 +1934,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name, struct socket *sock; int rc = 0; - if (value == NULL || size > SMK_LABELLEN || size == 0) + if (value == NULL || size > SMK_LONGLABEL || size == 0) return -EACCES; sp = smk_import(value, size); @@ -2785,7 +2730,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (value == NULL || size == 0 || size >= SMK_LABELLEN) + if (value == NULL || size == 0 || size >= SMK_LONGLABEL) return -EINVAL; if (strcmp(name, "current") != 0) @@ -2921,10 +2866,9 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg, static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, struct socket_smack *ssp) { - struct smack_known *skp; - char smack[SMK_LABELLEN]; + struct smack_known *kp; char *sp; - int pcat; + int found = 0; if ((sap->flags & NETLBL_SECATTR_MLS_LVL) != 0) { /* @@ -2932,59 +2876,27 @@ static char *smack_from_secattr(struct netlbl_lsm_secattr *sap, * If there are flags but no level netlabel isn't * behaving the way we expect it to. * - * Get the categories, if any + * Look it up in the label table * Without guidance regarding the smack value * for the packet fall back on the network * ambient value. */ - memset(smack, '\0', SMK_LABELLEN); - if ((sap->flags & NETLBL_SECATTR_MLS_CAT) != 0) - for (pcat = -1;;) { - pcat = netlbl_secattr_catmap_walk( - sap->attr.mls.cat, pcat + 1); - if (pcat < 0) - break; - smack_catset_bit(pcat, smack); - } - /* - * If it is CIPSO using smack direct mapping - * we are already done. WeeHee. - */ - if (sap->attr.mls.lvl == smack_cipso_direct) { - /* - * The label sent is usually on the label list. - * - * If it is not we may still want to allow the - * delivery. - * - * If the recipient is accepting all packets - * because it is using the star ("*") label - * for SMACK64IPIN provide the web ("@") label - * so that a directed response will succeed. - * This is not very correct from a MAC point - * of view, but gets around the problem that - * locking prevents adding the newly discovered - * label to the list. - * The case where the recipient is not using - * the star label should obviously fail. - * The easy way to do this is to provide the - * star label as the subject label. - */ - skp = smk_find_entry(smack); - if (skp != NULL) - return skp->smk_known; - if (ssp != NULL && - ssp->smk_in == smack_known_star.smk_known) - return smack_known_web.smk_known; - return smack_known_star.smk_known; + rcu_read_lock(); + list_for_each_entry(kp, &smack_known_list, list) { + if (sap->attr.mls.lvl != kp->smk_netlabel.attr.mls.lvl) + continue; + if (memcmp(sap->attr.mls.cat, + kp->smk_netlabel.attr.mls.cat, + SMK_CIPSOLEN) != 0) + continue; + found = 1; + break; } - /* - * Look it up in the supplied table if it is not - * a direct mapping. - */ - sp = smack_from_cipso(sap->attr.mls.lvl, smack); - if (sp != NULL) - return sp; + rcu_read_unlock(); + + if (found) + return kp->smk_known; + if (ssp != NULL && ssp->smk_in == smack_known_star.smk_known) return smack_known_web.smk_known; return smack_known_star.smk_known; @@ -3184,11 +3096,13 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, struct request_sock *req) { u16 family = sk->sk_family; + struct smack_known *skp; struct socket_smack *ssp = sk->sk_security; struct netlbl_lsm_secattr secattr; struct sockaddr_in addr; struct iphdr *hdr; char *sp; + char *hsp; int rc; struct smk_audit_info ad; #ifdef CONFIG_AUDIT @@ -3235,16 +3149,14 @@ static int smack_inet_conn_request(struct sock *sk, struct sk_buff *skb, hdr = ip_hdr(skb); addr.sin_addr.s_addr = hdr->saddr; rcu_read_lock(); - if (smack_host_label(&addr) == NULL) { - rcu_read_unlock(); - netlbl_secattr_init(&secattr); - smack_to_secattr(sp, &secattr); - rc = netlbl_req_setattr(req, &secattr); - netlbl_secattr_destroy(&secattr); - } else { - rcu_read_unlock(); + hsp = smack_host_label(&addr); + rcu_read_unlock(); + + if (hsp == NULL) { + skp = smk_find_entry(sp); + rc = netlbl_req_setattr(req, &skp->smk_netlabel); + } else netlbl_req_delattr(req); - } return rc; } @@ -3668,15 +3580,6 @@ struct security_operations smack_ops = { static __init void init_smack_known_list(void) { - /* - * Initialize CIPSO locks - */ - spin_lock_init(&smack_known_huh.smk_cipsolock); - spin_lock_init(&smack_known_hat.smk_cipsolock); - spin_lock_init(&smack_known_star.smk_cipsolock); - spin_lock_init(&smack_known_floor.smk_cipsolock); - spin_lock_init(&smack_known_invalid.smk_cipsolock); - spin_lock_init(&smack_known_web.smk_cipsolock); /* * Initialize rule list locks */ diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 038811cb7e62..1810c9a4ed48 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -45,6 +44,11 @@ enum smk_inos { SMK_LOGGING = 10, /* logging */ SMK_LOAD_SELF = 11, /* task specific rules */ SMK_ACCESSES = 12, /* access policy */ + SMK_MAPPED = 13, /* CIPSO level indicating mapped label */ + SMK_LOAD2 = 14, /* load policy with long labels */ + SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */ + SMK_ACCESS2 = 16, /* make an access check with long labels */ + SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ }; /* @@ -60,7 +64,7 @@ static DEFINE_MUTEX(smk_netlbladdr_lock); * If it isn't somehow marked, use this. * It can be reset via smackfs/ambient */ -char *smack_net_ambient = smack_known_floor.smk_known; +char *smack_net_ambient; /* * This is the level in a CIPSO header that indicates a @@ -69,6 +73,13 @@ char *smack_net_ambient = smack_known_floor.smk_known; */ int smack_cipso_direct = SMACK_CIPSO_DIRECT_DEFAULT; +/* + * This is the level in a CIPSO header that indicates a + * secid is contained directly in the category set. + * It can be reset via smackfs/mapped + */ +int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; + /* * Unless a process is running with this label even * having CAP_MAC_OVERRIDE isn't enough to grant @@ -89,7 +100,7 @@ LIST_HEAD(smk_netlbladdr_list); /* * Rule lists are maintained for each label. - * This master list is just for reading /smack/load. + * This master list is just for reading /smack/load and /smack/load2. */ struct smack_master_list { struct list_head list; @@ -125,6 +136,18 @@ const char *smack_cipso_option = SMACK_CIPSO_OPTION; #define SMK_OLOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_OACCESSLEN) #define SMK_LOADLEN (SMK_LABELLEN + SMK_LABELLEN + SMK_ACCESSLEN) +/* + * Stricly for CIPSO level manipulation. + * Set the category bit number in a smack label sized buffer. + */ +static inline void smack_catset_bit(unsigned int cat, char *catsetp) +{ + if (cat == 0 || cat > (SMK_CIPSOLEN * 8)) + return; + + catsetp[(cat - 1) / 8] |= 0x80 >> ((cat - 1) % 8); +} + /** * smk_netlabel_audit_set - fill a netlbl_audit struct * @nap: structure to fill @@ -137,12 +160,10 @@ static void smk_netlabel_audit_set(struct netlbl_audit *nap) } /* - * Values for parsing single label host rules + * Value for parsing single label host rules * "1.2.3.4 X" - * "192.168.138.129/32 abcdefghijklmnopqrstuvw" */ #define SMK_NETLBLADDRMIN 9 -#define SMK_NETLBLADDRMAX 42 /** * smk_set_access - add a rule to the rule list @@ -188,33 +209,47 @@ static int smk_set_access(struct smack_rule *srp, struct list_head *rule_list, } /** - * smk_parse_rule - parse Smack rule from load string - * @data: string to be parsed whose size is SMK_LOADLEN + * smk_fill_rule - Fill Smack rule from strings + * @subject: subject label string + * @object: object label string + * @access: access string * @rule: Smack rule * @import: if non-zero, import labels + * + * Returns 0 on success, -1 on failure */ -static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +static int smk_fill_rule(const char *subject, const char *object, + const char *access, struct smack_rule *rule, + int import) { - char smack[SMK_LABELLEN]; + int rc = -1; + int done; + const char *cp; struct smack_known *skp; if (import) { - rule->smk_subject = smk_import(data, 0); + rule->smk_subject = smk_import(subject, 0); if (rule->smk_subject == NULL) return -1; - rule->smk_object = smk_import(data + SMK_LABELLEN, 0); + rule->smk_object = smk_import(object, 0); if (rule->smk_object == NULL) return -1; } else { - smk_parse_smack(data, 0, smack); - skp = smk_find_entry(smack); + cp = smk_parse_smack(subject, 0); + if (cp == NULL) + return -1; + skp = smk_find_entry(cp); + kfree(cp); if (skp == NULL) return -1; rule->smk_subject = skp->smk_known; - smk_parse_smack(data + SMK_LABELLEN, 0, smack); - skp = smk_find_entry(smack); + cp = smk_parse_smack(object, 0); + if (cp == NULL) + return -1; + skp = smk_find_entry(cp); + kfree(cp); if (skp == NULL) return -1; rule->smk_object = skp->smk_known; @@ -222,90 +257,127 @@ static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) rule->smk_access = 0; - switch (data[SMK_LABELLEN + SMK_LABELLEN]) { - case '-': - break; - case 'r': - case 'R': - rule->smk_access |= MAY_READ; - break; - default: - return -1; + for (cp = access, done = 0; *cp && !done; cp++) { + switch (*cp) { + case '-': + break; + case 'r': + case 'R': + rule->smk_access |= MAY_READ; + break; + case 'w': + case 'W': + rule->smk_access |= MAY_WRITE; + break; + case 'x': + case 'X': + rule->smk_access |= MAY_EXEC; + break; + case 'a': + case 'A': + rule->smk_access |= MAY_APPEND; + break; + case 't': + case 'T': + rule->smk_access |= MAY_TRANSMUTE; + break; + default: + done = 1; + break; + } } + rc = 0; - switch (data[SMK_LABELLEN + SMK_LABELLEN + 1]) { - case '-': - break; - case 'w': - case 'W': - rule->smk_access |= MAY_WRITE; - break; - default: - return -1; - } + return rc; +} - switch (data[SMK_LABELLEN + SMK_LABELLEN + 2]) { - case '-': - break; - case 'x': - case 'X': - rule->smk_access |= MAY_EXEC; - break; - default: - return -1; - } +/** + * smk_parse_rule - parse Smack rule from load string + * @data: string to be parsed whose size is SMK_LOADLEN + * @rule: Smack rule + * @import: if non-zero, import labels + * + * Returns 0 on success, -1 on errors. + */ +static int smk_parse_rule(const char *data, struct smack_rule *rule, int import) +{ + int rc; - switch (data[SMK_LABELLEN + SMK_LABELLEN + 3]) { - case '-': - break; - case 'a': - case 'A': - rule->smk_access |= MAY_APPEND; - break; - default: - return -1; - } + rc = smk_fill_rule(data, data + SMK_LABELLEN, + data + SMK_LABELLEN + SMK_LABELLEN, rule, import); + return rc; +} - switch (data[SMK_LABELLEN + SMK_LABELLEN + 4]) { - case '-': - break; - case 't': - case 'T': - rule->smk_access |= MAY_TRANSMUTE; - break; - default: - return -1; - } +/** + * smk_parse_long_rule - parse Smack rule from rule string + * @data: string to be parsed, null terminated + * @rule: Smack rule + * @import: if non-zero, import labels + * + * Returns 0 on success, -1 on failure + */ +static int smk_parse_long_rule(const char *data, struct smack_rule *rule, + int import) +{ + char *subject; + char *object; + char *access; + int datalen; + int rc = -1; - return 0; + /* + * This is probably inefficient, but safe. + */ + datalen = strlen(data); + subject = kzalloc(datalen, GFP_KERNEL); + if (subject == NULL) + return -1; + object = kzalloc(datalen, GFP_KERNEL); + if (object == NULL) + goto free_out_s; + access = kzalloc(datalen, GFP_KERNEL); + if (access == NULL) + goto free_out_o; + + if (sscanf(data, "%s %s %s", subject, object, access) == 3) + rc = smk_fill_rule(subject, object, access, rule, import); + + kfree(access); +free_out_o: + kfree(object); +free_out_s: + kfree(subject); + return rc; } +#define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ +#define SMK_LONG_FMT 1 /* Variable long label format */ /** - * smk_write_load_list - write() for any /smack/load + * smk_write_rules_list - write() for any /smack rule file * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start - must be 0 * @rule_list: the list of rules to write to * @rule_lock: lock for the rule list + * @format: /smack/load or /smack/load2 format. * * Get one smack access rule from above. - * The format is exactly: - * char subject[SMK_LABELLEN] - * char object[SMK_LABELLEN] - * char access[SMK_ACCESSLEN] - * - * writes must be SMK_LABELLEN+SMK_LABELLEN+SMK_ACCESSLEN bytes. + * The format for SMK_LONG_FMT is: + * "subjectobjectaccess[...]" + * The format for SMK_FIXED24_FMT is exactly: + * "subject object rwxat" */ -static ssize_t smk_write_load_list(struct file *file, const char __user *buf, - size_t count, loff_t *ppos, - struct list_head *rule_list, - struct mutex *rule_lock) +static ssize_t smk_write_rules_list(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, + struct list_head *rule_list, + struct mutex *rule_lock, int format) { struct smack_master_list *smlp; struct smack_known *skp; struct smack_rule *rule; char *data; + int datalen; int rc = -EINVAL; int load = 0; @@ -315,13 +387,18 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, */ if (*ppos != 0) return -EINVAL; - /* - * Minor hack for backward compatibility - */ - if (count < (SMK_OLOADLEN) || count > SMK_LOADLEN) - return -EINVAL; - data = kzalloc(SMK_LOADLEN, GFP_KERNEL); + if (format == SMK_FIXED24_FMT) { + /* + * Minor hack for backward compatibility + */ + if (count != SMK_OLOADLEN && count != SMK_LOADLEN) + return -EINVAL; + datalen = SMK_LOADLEN; + } else + datalen = count + 1; + + data = kzalloc(datalen, GFP_KERNEL); if (data == NULL) return -ENOMEM; @@ -330,20 +407,29 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, goto out; } - /* - * More on the minor hack for backward compatibility - */ - if (count == (SMK_OLOADLEN)) - data[SMK_OLOADLEN] = '-'; - rule = kzalloc(sizeof(*rule), GFP_KERNEL); if (rule == NULL) { rc = -ENOMEM; goto out; } - if (smk_parse_rule(data, rule, 1)) - goto out_free_rule; + if (format == SMK_LONG_FMT) { + /* + * Be sure the data string is terminated. + */ + data[count] = '\0'; + if (smk_parse_long_rule(data, rule, 1)) + goto out_free_rule; + } else { + /* + * More on the minor hack for backward compatibility + */ + if (count == (SMK_OLOADLEN)) + data[SMK_OLOADLEN] = '-'; + if (smk_parse_rule(data, rule, 1)) + goto out_free_rule; + } + if (rule_list == NULL) { load = 1; @@ -354,18 +440,20 @@ static ssize_t smk_write_load_list(struct file *file, const char __user *buf, rc = count; /* - * If this is "load" as opposed to "load-self" and a new rule + * If this is a global as opposed to self and a new rule * it needs to get added for reporting. * smk_set_access returns true if there was already a rule * for the subject/object pair, and false if it was new. */ - if (load && !smk_set_access(rule, rule_list, rule_lock)) { - smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); - if (smlp != NULL) { - smlp->smk_rule = rule; - list_add_rcu(&smlp->list, &smack_rule_list); - } else - rc = -ENOMEM; + if (!smk_set_access(rule, rule_list, rule_lock)) { + if (load) { + smlp = kzalloc(sizeof(*smlp), GFP_KERNEL); + if (smlp != NULL) { + smlp->smk_rule = rule; + list_add_rcu(&smlp->list, &smack_rule_list); + } else + rc = -ENOMEM; + } goto out; } @@ -421,29 +509,18 @@ static void smk_seq_stop(struct seq_file *s, void *v) /* No-op */ } -/* - * Seq_file read operations for /smack/load - */ - -static void *load_seq_start(struct seq_file *s, loff_t *pos) -{ - return smk_seq_start(s, pos, &smack_rule_list); -} - -static void *load_seq_next(struct seq_file *s, void *v, loff_t *pos) +static void smk_rule_show(struct seq_file *s, struct smack_rule *srp, int max) { - return smk_seq_next(s, v, pos, &smack_rule_list); -} - -static int load_seq_show(struct seq_file *s, void *v) -{ - struct list_head *list = v; - struct smack_master_list *smlp = - list_entry(list, struct smack_master_list, list); - struct smack_rule *srp = smlp->smk_rule; + /* + * Don't show any rules with label names too long for + * interface file (/smack/load or /smack/load2) + * because you should expect to be able to write + * anything you read back. + */ + if (strlen(srp->smk_subject) >= max || strlen(srp->smk_object) >= max) + return; - seq_printf(s, "%s %s", (char *)srp->smk_subject, - (char *)srp->smk_object); + seq_printf(s, "%s %s", srp->smk_subject, srp->smk_object); seq_putc(s, ' '); @@ -461,13 +538,36 @@ static int load_seq_show(struct seq_file *s, void *v) seq_putc(s, '-'); seq_putc(s, '\n'); +} + +/* + * Seq_file read operations for /smack/load + */ + +static void *load2_seq_start(struct seq_file *s, loff_t *pos) +{ + return smk_seq_start(s, pos, &smack_rule_list); +} + +static void *load2_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return smk_seq_next(s, v, pos, &smack_rule_list); +} + +static int load_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_master_list *smlp = + list_entry(list, struct smack_master_list, list); + + smk_rule_show(s, smlp->smk_rule, SMK_LABELLEN); return 0; } static const struct seq_operations load_seq_ops = { - .start = load_seq_start, - .next = load_seq_next, + .start = load2_seq_start, + .next = load2_seq_next, .show = load_seq_show, .stop = smk_seq_stop, }; @@ -504,7 +604,8 @@ static ssize_t smk_write_load(struct file *file, const char __user *buf, if (!capable(CAP_MAC_ADMIN)) return -EPERM; - return smk_write_load_list(file, buf, count, ppos, NULL, NULL); + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_FIXED24_FMT); } static const struct file_operations smk_load_ops = { @@ -574,6 +675,8 @@ static void smk_unlbl_ambient(char *oldambient) printk(KERN_WARNING "%s:%d remove rc = %d\n", __func__, __LINE__, rc); } + if (smack_net_ambient == NULL) + smack_net_ambient = smack_known_floor.smk_known; rc = netlbl_cfg_unlbl_map_add(smack_net_ambient, PF_INET, NULL, NULL, &nai); @@ -605,27 +708,28 @@ static int cipso_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smack_known *skp = list_entry(list, struct smack_known, list); - struct smack_cipso *scp = skp->smk_cipso; - char *cbp; + struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; - int cat = 1; int i; - unsigned char m; - if (scp == NULL) + /* + * Don't show a label that could not have been set using + * /smack/cipso. This is in support of the notion that + * anything read from /smack/cipso ought to be writeable + * to /smack/cipso. + * + * /smack/cipso2 should be used instead. + */ + if (strlen(skp->smk_known) >= SMK_LABELLEN) return 0; - seq_printf(s, "%s %3d", (char *)&skp->smk_known, scp->smk_level); + seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - cbp = scp->smk_catset; - for (i = 0; i < SMK_LABELLEN; i++) - for (m = 0x80; m != 0; m >>= 1) { - if (m & cbp[i]) { - seq_printf(s, "%c%d", sep, cat); - sep = ','; - } - cat++; - } + for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; + i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + seq_printf(s, "%c%d", sep, i); + sep = ','; + } seq_putc(s, '\n'); @@ -653,23 +757,24 @@ static int smk_open_cipso(struct inode *inode, struct file *file) } /** - * smk_write_cipso - write() for /smack/cipso + * smk_set_cipso - do the work for write() for cipso and cipso2 * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent * @ppos: where to start + * @format: /smack/cipso or /smack/cipso2 * * Accepts only one cipso rule per write call. * Returns number of bytes written or error code, as appropriate */ -static ssize_t smk_write_cipso(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t smk_set_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, int format) { struct smack_known *skp; - struct smack_cipso *scp = NULL; - char mapcatset[SMK_LABELLEN]; + struct netlbl_lsm_secattr ncats; + char mapcatset[SMK_CIPSOLEN]; int maplevel; - int cat; + unsigned int cat; int catlen; ssize_t rc = -EINVAL; char *data = NULL; @@ -686,7 +791,8 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, return -EPERM; if (*ppos != 0) return -EINVAL; - if (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX) + if (format == SMK_FIXED24_FMT && + (count < SMK_CIPSOMIN || count > SMK_CIPSOMAX)) return -EINVAL; data = kzalloc(count + 1, GFP_KERNEL); @@ -698,11 +804,6 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, goto unlockedout; } - /* labels cannot begin with a '-' */ - if (data[0] == '-') { - rc = -EINVAL; - goto unlockedout; - } data[count] = '\0'; rule = data; /* @@ -715,7 +816,11 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, if (skp == NULL) goto out; - rule += SMK_LABELLEN; + if (format == SMK_FIXED24_FMT) + rule += SMK_LABELLEN; + else + rule += strlen(skp->smk_known); + ret = sscanf(rule, "%d", &maplevel); if (ret != 1 || maplevel > SMACK_CIPSO_MAXLEVEL) goto out; @@ -725,41 +830,29 @@ static ssize_t smk_write_cipso(struct file *file, const char __user *buf, if (ret != 1 || catlen > SMACK_CIPSO_MAXCATNUM) goto out; - if (count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN)) + if (format == SMK_FIXED24_FMT && + count != (SMK_CIPSOMIN + catlen * SMK_DIGITLEN)) goto out; memset(mapcatset, 0, sizeof(mapcatset)); for (i = 0; i < catlen; i++) { rule += SMK_DIGITLEN; - ret = sscanf(rule, "%d", &cat); + ret = sscanf(rule, "%u", &cat); if (ret != 1 || cat > SMACK_CIPSO_MAXCATVAL) goto out; smack_catset_bit(cat, mapcatset); } - if (skp->smk_cipso == NULL) { - scp = kzalloc(sizeof(struct smack_cipso), GFP_KERNEL); - if (scp == NULL) { - rc = -ENOMEM; - goto out; - } + rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN); + if (rc >= 0) { + netlbl_secattr_catmap_free(skp->smk_netlabel.attr.mls.cat); + skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat; + skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl; + rc = count; } - spin_lock_bh(&skp->smk_cipsolock); - - if (scp == NULL) - scp = skp->smk_cipso; - else - skp->smk_cipso = scp; - - scp->smk_level = maplevel; - memcpy(scp->smk_catset, mapcatset, sizeof(mapcatset)); - - spin_unlock_bh(&skp->smk_cipsolock); - - rc = count; out: mutex_unlock(&smack_cipso_lock); unlockedout: @@ -767,6 +860,22 @@ unlockedout: return rc; } +/** + * smk_write_cipso - write() for /smack/cipso + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Accepts only one cipso rule per write call. + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_cipso(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_set_cipso(file, buf, count, ppos, SMK_FIXED24_FMT); +} + static const struct file_operations smk_cipso_ops = { .open = smk_open_cipso, .read = seq_read, @@ -775,6 +884,80 @@ static const struct file_operations smk_cipso_ops = { .release = seq_release, }; +/* + * Seq_file read operations for /smack/cipso2 + */ + +/* + * Print cipso labels in format: + * label level[/cat[,cat]] + */ +static int cipso2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_known *skp = + list_entry(list, struct smack_known, list); + struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + char sep = '/'; + int i; + + seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); + + for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; + i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + seq_printf(s, "%c%d", sep, i); + sep = ','; + } + + seq_putc(s, '\n'); + + return 0; +} + +static const struct seq_operations cipso2_seq_ops = { + .start = cipso_seq_start, + .next = cipso_seq_next, + .show = cipso2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_cipso2 - open() for /smack/cipso2 + * @inode: inode structure representing file + * @file: "cipso2" file pointer + * + * Connect our cipso_seq_* operations with /smack/cipso2 + * file_operations + */ +static int smk_open_cipso2(struct inode *inode, struct file *file) +{ + return seq_open(file, &cipso2_seq_ops); +} + +/** + * smk_write_cipso2 - write() for /smack/cipso2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Accepts only one cipso rule per write call. + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_cipso2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_set_cipso(file, buf, count, ppos, SMK_LONG_FMT); +} + +static const struct file_operations smk_cipso2_ops = { + .open = smk_open_cipso2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_cipso2, + .release = seq_release, +}; + /* * Seq_file read operations for /smack/netlabel */ @@ -887,9 +1070,9 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, { struct smk_netlbladdr *skp; struct sockaddr_in newname; - char smack[SMK_LABELLEN]; + char *smack; char *sp; - char data[SMK_NETLBLADDRMAX + 1]; + char *data; char *host = (char *)&newname.sin_addr.s_addr; int rc; struct netlbl_audit audit_info; @@ -911,10 +1094,23 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, return -EPERM; if (*ppos != 0) return -EINVAL; - if (count < SMK_NETLBLADDRMIN || count > SMK_NETLBLADDRMAX) + if (count < SMK_NETLBLADDRMIN) return -EINVAL; - if (copy_from_user(data, buf, count) != 0) - return -EFAULT; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto free_data_out; + } + + smack = kzalloc(count + 1, GFP_KERNEL); + if (smack == NULL) { + rc = -ENOMEM; + goto free_data_out; + } data[count] = '\0'; @@ -923,24 +1119,34 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, if (rc != 6) { rc = sscanf(data, "%hhd.%hhd.%hhd.%hhd %s", &host[0], &host[1], &host[2], &host[3], smack); - if (rc != 5) - return -EINVAL; + if (rc != 5) { + rc = -EINVAL; + goto free_out; + } m = BEBITS; } - if (m > BEBITS) - return -EINVAL; + if (m > BEBITS) { + rc = -EINVAL; + goto free_out; + } - /* if smack begins with '-', its an option, don't import it */ + /* + * If smack begins with '-', it is an option, don't import it + */ if (smack[0] != '-') { sp = smk_import(smack, 0); - if (sp == NULL) - return -EINVAL; + if (sp == NULL) { + rc = -EINVAL; + goto free_out; + } } else { /* check known options */ if (strcmp(smack, smack_cipso_option) == 0) sp = (char *)smack_cipso_option; - else - return -EINVAL; + else { + rc = -EINVAL; + goto free_out; + } } for (temp_mask = 0; m > 0; m--) { @@ -1006,6 +1212,11 @@ static ssize_t smk_write_netlbladdr(struct file *file, const char __user *buf, mutex_unlock(&smk_netlbladdr_lock); +free_out: + kfree(smack); +free_data_out: + kfree(data); + return rc; } @@ -1119,6 +1330,7 @@ static ssize_t smk_read_direct(struct file *filp, char __user *buf, static ssize_t smk_write_direct(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { + struct smack_known *skp; char temp[80]; int i; @@ -1136,7 +1348,20 @@ static ssize_t smk_write_direct(struct file *file, const char __user *buf, if (sscanf(temp, "%d", &i) != 1) return -EINVAL; - smack_cipso_direct = i; + /* + * Don't do anything if the value hasn't actually changed. + * If it is changing reset the level on entries that were + * set up to be direct when they were created. + */ + if (smack_cipso_direct != i) { + mutex_lock(&smack_known_lock); + list_for_each_entry_rcu(skp, &smack_known_list, list) + if (skp->smk_netlabel.attr.mls.lvl == + smack_cipso_direct) + skp->smk_netlabel.attr.mls.lvl = i; + smack_cipso_direct = i; + mutex_unlock(&smack_known_lock); + } return count; } @@ -1147,6 +1372,84 @@ static const struct file_operations smk_direct_ops = { .llseek = default_llseek, }; +/** + * smk_read_mapped - read() for /smack/mapped + * @filp: file pointer, not actually used + * @buf: where to put the result + * @count: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_mapped(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + char temp[80]; + ssize_t rc; + + if (*ppos != 0) + return 0; + + sprintf(temp, "%d", smack_cipso_mapped); + rc = simple_read_from_buffer(buf, count, ppos, temp, strlen(temp)); + + return rc; +} + +/** + * smk_write_mapped - write() for /smack/mapped + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_mapped(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct smack_known *skp; + char temp[80]; + int i; + + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + if (count >= sizeof(temp) || count == 0) + return -EINVAL; + + if (copy_from_user(temp, buf, count) != 0) + return -EFAULT; + + temp[count] = '\0'; + + if (sscanf(temp, "%d", &i) != 1) + return -EINVAL; + + /* + * Don't do anything if the value hasn't actually changed. + * If it is changing reset the level on entries that were + * set up to be mapped when they were created. + */ + if (smack_cipso_mapped != i) { + mutex_lock(&smack_known_lock); + list_for_each_entry_rcu(skp, &smack_known_list, list) + if (skp->smk_netlabel.attr.mls.lvl == + smack_cipso_mapped) + skp->smk_netlabel.attr.mls.lvl = i; + smack_cipso_mapped = i; + mutex_unlock(&smack_known_lock); + } + + return count; +} + +static const struct file_operations smk_mapped_ops = { + .read = smk_read_mapped, + .write = smk_write_mapped, + .llseek = default_llseek, +}; + /** * smk_read_ambient - read() for /smack/ambient * @filp: file pointer, not actually used @@ -1195,22 +1498,28 @@ static ssize_t smk_read_ambient(struct file *filp, char __user *buf, static ssize_t smk_write_ambient(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char in[SMK_LABELLEN]; char *oldambient; - char *smack; + char *smack = NULL; + char *data; + int rc = count; if (!capable(CAP_MAC_ADMIN)) return -EPERM; - if (count >= SMK_LABELLEN) - return -EINVAL; + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; - if (copy_from_user(in, buf, count) != 0) - return -EFAULT; + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto out; + } - smack = smk_import(in, count); - if (smack == NULL) - return -EINVAL; + smack = smk_import(data, count); + if (smack == NULL) { + rc = -EINVAL; + goto out; + } mutex_lock(&smack_ambient_lock); @@ -1220,7 +1529,9 @@ static ssize_t smk_write_ambient(struct file *file, const char __user *buf, mutex_unlock(&smack_ambient_lock); - return count; +out: + kfree(data); + return rc; } static const struct file_operations smk_ambient_ops = { @@ -1271,8 +1582,9 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - char in[SMK_LABELLEN]; + char *data; char *sp = smk_of_task(current->cred->security); + int rc = count; if (!capable(CAP_MAC_ADMIN)) return -EPERM; @@ -1285,11 +1597,9 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (smack_onlycap != NULL && smack_onlycap != sp) return -EPERM; - if (count >= SMK_LABELLEN) - return -EINVAL; - - if (copy_from_user(in, buf, count) != 0) - return -EFAULT; + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; /* * Should the null string be passed in unset the onlycap value. @@ -1297,10 +1607,17 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * smk_import only expects to return NULL for errors. It * is usually the case that a nullstring or "\n" would be * bad to pass to smk_import but in fact this is useful here. + * + * smk_import will also reject a label beginning with '-', + * so "-usecapabilities" will also work. */ - smack_onlycap = smk_import(in, count); + if (copy_from_user(data, buf, count) != 0) + rc = -EFAULT; + else + smack_onlycap = smk_import(data, count); - return count; + kfree(data); + return rc; } static const struct file_operations smk_onlycap_ops = { @@ -1398,25 +1715,7 @@ static int load_self_seq_show(struct seq_file *s, void *v) struct smack_rule *srp = list_entry(list, struct smack_rule, list); - seq_printf(s, "%s %s", (char *)srp->smk_subject, - (char *)srp->smk_object); - - seq_putc(s, ' '); - - if (srp->smk_access & MAY_READ) - seq_putc(s, 'r'); - if (srp->smk_access & MAY_WRITE) - seq_putc(s, 'w'); - if (srp->smk_access & MAY_EXEC) - seq_putc(s, 'x'); - if (srp->smk_access & MAY_APPEND) - seq_putc(s, 'a'); - if (srp->smk_access & MAY_TRANSMUTE) - seq_putc(s, 't'); - if (srp->smk_access == 0) - seq_putc(s, '-'); - - seq_putc(s, '\n'); + smk_rule_show(s, srp, SMK_LABELLEN); return 0; } @@ -1430,7 +1729,7 @@ static const struct seq_operations load_self_seq_ops = { /** - * smk_open_load_self - open() for /smack/load-self + * smk_open_load_self - open() for /smack/load-self2 * @inode: inode structure representing file * @file: "load" file pointer * @@ -1454,8 +1753,8 @@ static ssize_t smk_write_load_self(struct file *file, const char __user *buf, { struct task_smack *tsp = current_security(); - return smk_write_load_list(file, buf, count, ppos, &tsp->smk_rules, - &tsp->smk_rules_lock); + return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, + &tsp->smk_rules_lock, SMK_FIXED24_FMT); } static const struct file_operations smk_load_self_ops = { @@ -1467,24 +1766,42 @@ static const struct file_operations smk_load_self_ops = { }; /** - * smk_write_access - handle access check transaction + * smk_user_access - handle access check transaction * @file: file pointer * @buf: data from user space * @count: bytes sent * @ppos: where to start - must be 0 */ -static ssize_t smk_write_access(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) +static ssize_t smk_user_access(struct file *file, const char __user *buf, + size_t count, loff_t *ppos, int format) { struct smack_rule rule; char *data; + char *cod; int res; data = simple_transaction_get(file, buf, count); if (IS_ERR(data)) return PTR_ERR(data); - if (count < SMK_LOADLEN || smk_parse_rule(data, &rule, 0)) + if (format == SMK_FIXED24_FMT) { + if (count < SMK_LOADLEN) + return -EINVAL; + res = smk_parse_rule(data, &rule, 0); + } else { + /* + * Copy the data to make sure the string is terminated. + */ + cod = kzalloc(count + 1, GFP_KERNEL); + if (cod == NULL) + return -ENOMEM; + memcpy(cod, data, count); + cod[count] = '\0'; + res = smk_parse_long_rule(cod, &rule, 0); + kfree(cod); + } + + if (res) return -EINVAL; res = smk_access(rule.smk_subject, rule.smk_object, rule.smk_access, @@ -1493,7 +1810,23 @@ static ssize_t smk_write_access(struct file *file, const char __user *buf, data[1] = '\0'; simple_transaction_set(file, 2); - return SMK_LOADLEN; + + if (format == SMK_FIXED24_FMT) + return SMK_LOADLEN; + return count; +} + +/** + * smk_write_access - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_access(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_user_access(file, buf, count, ppos, SMK_FIXED24_FMT); } static const struct file_operations smk_access_ops = { @@ -1503,6 +1836,163 @@ static const struct file_operations smk_access_ops = { .llseek = generic_file_llseek, }; + +/* + * Seq_file read operations for /smack/load2 + */ + +static int load2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_master_list *smlp = + list_entry(list, struct smack_master_list, list); + + smk_rule_show(s, smlp->smk_rule, SMK_LONGLABEL); + + return 0; +} + +static const struct seq_operations load2_seq_ops = { + .start = load2_seq_start, + .next = load2_seq_next, + .show = load2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_load2 - open() for /smack/load2 + * @inode: inode structure representing file + * @file: "load2" file pointer + * + * For reading, use load2_seq_* seq_file reading operations. + */ +static int smk_open_load2(struct inode *inode, struct file *file) +{ + return seq_open(file, &load2_seq_ops); +} + +/** + * smk_write_load2 - write() for /smack/load2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + /* + * Must have privilege. + */ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, + SMK_LONG_FMT); +} + +static const struct file_operations smk_load2_ops = { + .open = smk_open_load2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_load2, + .release = seq_release, +}; + +/* + * Seq_file read operations for /smack/load-self2 + */ + +static void *load_self2_seq_start(struct seq_file *s, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_start(s, pos, &tsp->smk_rules); +} + +static void *load_self2_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_next(s, v, pos, &tsp->smk_rules); +} + +static int load_self2_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_rule *srp = + list_entry(list, struct smack_rule, list); + + smk_rule_show(s, srp, SMK_LONGLABEL); + + return 0; +} + +static const struct seq_operations load_self2_seq_ops = { + .start = load_self2_seq_start, + .next = load_self2_seq_next, + .show = load_self2_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_load_self2 - open() for /smack/load-self2 + * @inode: inode structure representing file + * @file: "load" file pointer + * + * For reading, use load_seq_* seq_file reading operations. + */ +static int smk_open_load_self2(struct inode *inode, struct file *file) +{ + return seq_open(file, &load_self2_seq_ops); +} + +/** + * smk_write_load_self2 - write() for /smack/load-self2 + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_load_self2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current_security(); + + return smk_write_rules_list(file, buf, count, ppos, &tsp->smk_rules, + &tsp->smk_rules_lock, SMK_LONG_FMT); +} + +static const struct file_operations smk_load_self2_ops = { + .open = smk_open_load_self2, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_load_self2, + .release = seq_release, +}; + +/** + * smk_write_access2 - handle access check transaction + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_access2(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return smk_user_access(file, buf, count, ppos, SMK_LONG_FMT); +} + +static const struct file_operations smk_access2_ops = { + .write = smk_write_access2, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -1539,6 +2029,16 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "load-self", &smk_load_self_ops, S_IRUGO|S_IWUGO}, [SMK_ACCESSES] = { "access", &smk_access_ops, S_IRUGO|S_IWUGO}, + [SMK_MAPPED] = { + "mapped", &smk_mapped_ops, S_IRUGO|S_IWUSR}, + [SMK_LOAD2] = { + "load2", &smk_load2_ops, S_IRUGO|S_IWUSR}, + [SMK_LOAD_SELF2] = { + "load-self2", &smk_load_self2_ops, S_IRUGO|S_IWUGO}, + [SMK_ACCESS2] = { + "access2", &smk_access2_ops, S_IRUGO|S_IWUGO}, + [SMK_CIPSO2] = { + "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; @@ -1581,6 +2081,15 @@ static struct file_system_type smk_fs_type = { static struct vfsmount *smackfs_mount; +static int __init smk_preset_netlabel(struct smack_known *skp) +{ + skp->smk_netlabel.domain = skp->smk_known; + skp->smk_netlabel.flags = + NETLBL_SECATTR_DOMAIN | NETLBL_SECATTR_MLS_LVL; + return smk_netlbl_mls(smack_cipso_direct, skp->smk_known, + &skp->smk_netlabel, strlen(skp->smk_known)); +} + /** * init_smk_fs - get the smackfs superblock * @@ -1597,6 +2106,7 @@ static struct vfsmount *smackfs_mount; static int __init init_smk_fs(void) { int err; + int rc; if (!security_module_enable(&smack_ops)) return 0; @@ -1614,6 +2124,25 @@ static int __init init_smk_fs(void) smk_cipso_doi(); smk_unlbl_ambient(NULL); + rc = smk_preset_netlabel(&smack_known_floor); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_hat); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_huh); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_invalid); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_star); + if (err == 0 && rc < 0) + err = rc; + rc = smk_preset_netlabel(&smack_known_web); + if (err == 0 && rc < 0) + err = rc; + return err; } -- cgit v1.2.2 From b404aef72fdafb601c945c714164c0ee2b04c364 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 15 May 2012 14:11:11 +0100 Subject: KEYS: Don't check for NULL key pointer in key_validate() Don't bother checking for NULL key pointer in key_validate() as all of the places that call it will crash anyway if the relevant key pointer is NULL by the time they call key_validate(). Therefore, the checking must be done prior to calling here. Whilst we're at it, simplify the key_validate() function a bit and mark its argument const. Reported-by: Dan Carpenter Signed-off-by: David Howells cc: Dan Carpenter Signed-off-by: James Morris --- security/keys/permission.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) (limited to 'security') diff --git a/security/keys/permission.c b/security/keys/permission.c index 5f4c00c0947d..57d96363d7f1 100644 --- a/security/keys/permission.c +++ b/security/keys/permission.c @@ -91,33 +91,25 @@ EXPORT_SYMBOL(key_task_permission); * key is invalidated, -EKEYREVOKED if the key's type has been removed or if * the key has been revoked or -EKEYEXPIRED if the key has expired. */ -int key_validate(struct key *key) +int key_validate(const struct key *key) { - struct timespec now; unsigned long flags = key->flags; - int ret = 0; - - if (key) { - ret = -ENOKEY; - if (flags & (1 << KEY_FLAG_INVALIDATED)) - goto error; - - /* check it's still accessible */ - ret = -EKEYREVOKED; - if (flags & ((1 << KEY_FLAG_REVOKED) | - (1 << KEY_FLAG_DEAD))) - goto error; - - /* check it hasn't expired */ - ret = 0; - if (key->expiry) { - now = current_kernel_time(); - if (now.tv_sec >= key->expiry) - ret = -EKEYEXPIRED; - } + + if (flags & (1 << KEY_FLAG_INVALIDATED)) + return -ENOKEY; + + /* check it's still accessible */ + if (flags & ((1 << KEY_FLAG_REVOKED) | + (1 << KEY_FLAG_DEAD))) + return -EKEYREVOKED; + + /* check it hasn't expired */ + if (key->expiry) { + struct timespec now = current_kernel_time(); + if (now.tv_sec >= key->expiry) + return -EKEYEXPIRED; } -error: - return ret; + return 0; } EXPORT_SYMBOL(key_validate); -- cgit v1.2.2 From fbbb456347b21279a379b42eeb31151c33d8dd49 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 14 May 2012 21:50:11 -0400 Subject: ima: fix filename hint to reflect script interpreter name When IMA was first upstreamed, the bprm filename and interp were always the same. Currently, the bprm->filename and bprm->interp are the same, except for when only bprm->interp contains the interpreter name. So instead of using the bprm->filename as the IMA filename hint in the measurement list, we could replace it with bprm->interp, but this feels too fragil. The following patch is not much better, but at least there is some indication that sometimes we're passing the filename and other times the interpreter name. Reported-by: Andrew Lunn Signed-off-by: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 1eff5cb001e5..b17be79b9cf2 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -194,7 +194,9 @@ int ima_bprm_check(struct linux_binprm *bprm) { int rc; - rc = process_measurement(bprm->file, bprm->filename, + rc = process_measurement(bprm->file, + (strcmp(bprm->filename, bprm->interp) == 0) ? + bprm->filename : bprm->interp, MAY_EXEC, BPRM_CHECK); return 0; } -- cgit v1.2.2 From bf83208e0b7f5938f5a7f6d9dfa9960bf04692fa Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 11:00:05 -0700 Subject: apparmor: fix profile lookup for unconfined BugLink: http://bugs.launchpad.net/bugs/978038 also affects apparmor portion of BugLink: http://bugs.launchpad.net/bugs/987371 The unconfined profile is not stored in the regular profile list, but change_profile and exec transitions may want access to it when setting up specialized transitions like switch to the unconfined profile of a new policy namespace. Signed-off-by: John Johansen --- security/apparmor/policy.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'security') diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index f1f7506a464d..7f3f455d8eab 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -903,6 +903,10 @@ struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) profile = aa_get_profile(__lookup_profile(&ns->base, hname)); read_unlock(&ns->lock); + /* the unconfined profile is not in the regular profile list */ + if (!profile && strcmp(hname, "unconfined") == 0) + profile = aa_get_profile(ns->unconfined); + /* refcount released by caller */ return profile; } -- cgit v1.2.2 From cffee16e8b997ab947de661e8820e486b0830c94 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 16 May 2012 11:01:05 -0700 Subject: apparmor: fix long path failure due to disconnected path BugLink: http://bugs.launchpad.net/bugs/955892 All failures from __d_path where being treated as disconnected paths, however __d_path can also fail when the generated pathname is too long. The initial ENAMETOOLONG error was being lost, and ENAMETOOLONG was only returned if the subsequent dentry_path call resulted in that error. Other wise if the path was split across a mount point such that the dentry_path fit within the buffer when the __d_path did not the failure was treated as a disconnected path. Signed-off-by: John Johansen --- security/apparmor/path.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 2daeea4f9266..e91ffee80162 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c @@ -94,6 +94,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, * be returned. */ if (!res || IS_ERR(res)) { + if (PTR_ERR(res) == -ENAMETOOLONG) + return -ENAMETOOLONG; connected = 0; res = dentry_path_raw(path->dentry, buf, buflen); if (IS_ERR(res)) { -- cgit v1.2.2 From 1227dd773d8d4e3983b4b751f9ffa0f41402fb7c Mon Sep 17 00:00:00 2001 From: Al Viro Date: Tue, 24 Apr 2012 02:44:49 -0400 Subject: TIF_NOTIFY_RESUME is defined on all targets now Signed-off-by: Al Viro --- security/keys/keyctl.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'security') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ddb3e05bc5fc..534a634283a4 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1454,7 +1454,6 @@ long keyctl_get_security(key_serial_t keyid, */ long keyctl_session_to_parent(void) { -#ifdef TIF_NOTIFY_RESUME struct task_struct *me, *parent; const struct cred *mycred, *pcred; struct cred *cred, *oldcred; @@ -1542,15 +1541,6 @@ not_permitted: error_keyring: key_ref_put(keyring_r); return ret; - -#else /* !TIF_NOTIFY_RESUME */ - /* - * To be removed when TIF_NOTIFY_RESUME has been implemented on - * m68k/xtensa - */ -#warning TIF_NOTIFY_RESUME not implemented - return -EOPNOTSUPP; -#endif /* !TIF_NOTIFY_RESUME */ } /* -- cgit v1.2.2 From 413cd3d9abeaef590e5ce00564f7a443165db238 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Fri, 11 May 2012 10:59:08 +1000 Subject: keys: change keyctl_session_to_parent() to use task_work_add() Change keyctl_session_to_parent() to use task_work_add() and move key_replace_session_keyring() logic into task_work->func(). Note that we do task_work_cancel() before task_work_add() to ensure that only one work can be pending at any time. This is important, we must not allow user-space to abuse the parent's ->task_works list. The callback, replace_session_keyring(), checks PF_EXITING. I guess this is not really needed but looks better. As a side effect, this fixes the (unlikely) race. The callers of key_replace_session_keyring() and keyctl_session_to_parent() lack the necessary barriers, the parent can miss the request. Now we can remove task_struct->replacement_session_keyring and related code. Signed-off-by: Oleg Nesterov Acked-by: David Howells Cc: Thomas Gleixner Cc: Richard Kuo Cc: Linus Torvalds Cc: Alexander Gordeev Cc: Chris Zankel Cc: David Smith Cc: "Frank Ch. Eigler" Cc: Geert Uytterhoeven Cc: Larry Woodman Cc: Peter Zijlstra Cc: Tejun Heo Cc: Ingo Molnar Signed-off-by: Andrew Morton Signed-off-by: Al Viro --- security/keys/internal.h | 2 ++ security/keys/keyctl.c | 63 ++++++++++++++++++++++++-------------------- security/keys/process_keys.c | 20 +++++--------- 3 files changed, 44 insertions(+), 41 deletions(-) (limited to 'security') diff --git a/security/keys/internal.h b/security/keys/internal.h index f711b094ed41..3dcbf86b0d31 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -14,6 +14,7 @@ #include #include +#include #ifdef __KDEBUG #define kenter(FMT, ...) \ @@ -148,6 +149,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, #define KEY_LOOKUP_FOR_UNLINK 0x04 extern long join_session_keyring(const char *name); +extern void key_change_session_keyring(struct task_work *twork); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 534a634283a4..2f28126215a2 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1456,47 +1456,55 @@ long keyctl_session_to_parent(void) { struct task_struct *me, *parent; const struct cred *mycred, *pcred; - struct cred *cred, *oldcred; + struct task_work *newwork, *oldwork; key_ref_t keyring_r; + struct cred *cred; int ret; keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); if (IS_ERR(keyring_r)) return PTR_ERR(keyring_r); + ret = -ENOMEM; + newwork = kmalloc(sizeof(struct task_work), GFP_KERNEL); + if (!newwork) + goto error_keyring; + /* our parent is going to need a new cred struct, a new tgcred struct * and new security data, so we allocate them here to prevent ENOMEM in * our parent */ - ret = -ENOMEM; cred = cred_alloc_blank(); if (!cred) - goto error_keyring; + goto error_newwork; cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); - keyring_r = NULL; + init_task_work(newwork, key_change_session_keyring, cred); me = current; rcu_read_lock(); write_lock_irq(&tasklist_lock); - parent = me->real_parent; ret = -EPERM; + oldwork = NULL; + parent = me->real_parent; /* the parent mustn't be init and mustn't be a kernel thread */ if (parent->pid <= 1 || !parent->mm) - goto not_permitted; + goto unlock; /* the parent must be single threaded */ if (!thread_group_empty(parent)) - goto not_permitted; + goto unlock; /* the parent and the child must have different session keyrings or * there's no point */ mycred = current_cred(); pcred = __task_cred(parent); if (mycred == pcred || - mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) - goto already_same; + mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) { + ret = 0; + goto unlock; + } /* the parent must have the same effective ownership and mustn't be * SUID/SGID */ @@ -1506,38 +1514,37 @@ long keyctl_session_to_parent(void) pcred->gid != mycred->egid || pcred->egid != mycred->egid || pcred->sgid != mycred->egid) - goto not_permitted; + goto unlock; /* the keyrings must have the same UID */ if ((pcred->tgcred->session_keyring && pcred->tgcred->session_keyring->uid != mycred->euid) || mycred->tgcred->session_keyring->uid != mycred->euid) - goto not_permitted; + goto unlock; - /* if there's an already pending keyring replacement, then we replace - * that */ - oldcred = parent->replacement_session_keyring; + /* cancel an already pending keyring replacement */ + oldwork = task_work_cancel(parent, key_change_session_keyring); /* the replacement session keyring is applied just prior to userspace * restarting */ - parent->replacement_session_keyring = cred; - cred = NULL; - set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); - - write_unlock_irq(&tasklist_lock); - rcu_read_unlock(); - if (oldcred) - put_cred(oldcred); - return 0; - -already_same: - ret = 0; -not_permitted: + ret = task_work_add(parent, newwork, true); + if (!ret) + newwork = NULL; +unlock: write_unlock_irq(&tasklist_lock); rcu_read_unlock(); - put_cred(cred); + if (oldwork) { + put_cred(oldwork->data); + kfree(oldwork); + } + if (newwork) { + put_cred(newwork->data); + kfree(newwork); + } return ret; +error_newwork: + kfree(newwork); error_keyring: key_ref_put(keyring_r); return ret; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index d71056db7b67..4ad54eea1ea4 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -834,23 +834,17 @@ error: * Replace a process's session keyring on behalf of one of its children when * the target process is about to resume userspace execution. */ -void key_replace_session_keyring(void) +void key_change_session_keyring(struct task_work *twork) { - const struct cred *old; - struct cred *new; - - if (!current->replacement_session_keyring) - return; + const struct cred *old = current_cred(); + struct cred *new = twork->data; - write_lock_irq(&tasklist_lock); - new = current->replacement_session_keyring; - current->replacement_session_keyring = NULL; - write_unlock_irq(&tasklist_lock); - - if (!new) + kfree(twork); + if (unlikely(current->flags & PF_EXITING)) { + put_cred(new); return; + } - old = current_cred(); new-> uid = old-> uid; new-> euid = old-> euid; new-> suid = old-> suid; -- cgit v1.2.2 From cc1dad7183e4cb7f5d313b6942f2059fc0eabab6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Mon, 2 Apr 2012 19:40:47 -0400 Subject: selinuxfs snprintf() misuses a) %d does _not_ produce a page worth of output b) snprintf() doesn't return negatives - it used to in old glibc, but that's the kernel... Signed-off-by: Al Viro --- security/selinux/selinuxfs.c | 36 +++++++----------------------------- 1 file changed, 7 insertions(+), 29 deletions(-) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 4e93f9ef970b..3ad290251288 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1259,12 +1259,8 @@ static int sel_make_bools(void) if (!inode) goto out; - ret = -EINVAL; - len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); - if (len < 0) - goto out; - ret = -ENAMETOOLONG; + len = snprintf(page, PAGE_SIZE, "/%s/%s", BOOL_DIR_NAME, names[i]); if (len >= PAGE_SIZE) goto out; @@ -1557,19 +1553,10 @@ static inline u32 sel_ino_to_perm(unsigned long ino) static ssize_t sel_read_class(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - ssize_t rc, len; - char *page; unsigned long ino = file->f_path.dentry->d_inode->i_ino; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_class(ino)); - rc = simple_read_from_buffer(buf, count, ppos, page, len); - free_page((unsigned long)page); - - return rc; + char res[TMPBUFLEN]; + ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_class(ino)); + return simple_read_from_buffer(buf, count, ppos, res, len); } static const struct file_operations sel_class_ops = { @@ -1580,19 +1567,10 @@ static const struct file_operations sel_class_ops = { static ssize_t sel_read_perm(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - ssize_t rc, len; - char *page; unsigned long ino = file->f_path.dentry->d_inode->i_ino; - - page = (char *)__get_free_page(GFP_KERNEL); - if (!page) - return -ENOMEM; - - len = snprintf(page, PAGE_SIZE, "%d", sel_ino_to_perm(ino)); - rc = simple_read_from_buffer(buf, count, ppos, page, len); - free_page((unsigned long)page); - - return rc; + char res[TMPBUFLEN]; + ssize_t len = snprintf(res, sizeof(res), "%d", sel_ino_to_perm(ino)); + return simple_read_from_buffer(buf, count, ppos, res, len); } static const struct file_operations sel_perm_ops = { -- cgit v1.2.2 From d007794a182bc072a7b7479909dbd0d67ba341be Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 May 2012 13:11:37 -0400 Subject: split cap_mmap_addr() out of cap_file_mmap() ... switch callers. Signed-off-by: Al Viro --- security/apparmor/lsm.c | 2 +- security/commoncap.c | 32 +++++++++++++++++++++++--------- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- 4 files changed, 26 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 032daab449b0..8430d8937afb 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -497,7 +497,7 @@ static int apparmor_file_mmap(struct file *file, unsigned long reqprot, int rc = 0; /* do DAC check */ - rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); + rc = cap_mmap_addr(addr); if (rc || addr_only) return rc; diff --git a/security/commoncap.c b/security/commoncap.c index e771cb1b2d79..ebac3618896e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -958,22 +958,15 @@ int cap_vm_enough_memory(struct mm_struct *mm, long pages) } /* - * cap_file_mmap - check if able to map given addr - * @file: unused - * @reqprot: unused - * @prot: unused - * @flags: unused + * cap_mmap_addr - check if able to map given addr * @addr: address attempting to be mapped - * @addr_only: unused * * If the process is attempting to map memory below dac_mmap_min_addr they need * CAP_SYS_RAWIO. The other parameters to this function are unused by the * capability security module. Returns 0 if this mapping should be allowed * -EPERM if not. */ -int cap_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags, - unsigned long addr, unsigned long addr_only) +int cap_mmap_addr(unsigned long addr) { int ret = 0; @@ -986,3 +979,24 @@ int cap_file_mmap(struct file *file, unsigned long reqprot, } return ret; } + +/* + * cap_file_mmap - check if able to map given addr + * @file: unused + * @reqprot: unused + * @prot: unused + * @flags: unused + * @addr: address attempting to be mapped + * @addr_only: unused + * + * If the process is attempting to map memory below dac_mmap_min_addr they need + * CAP_SYS_RAWIO. The other parameters to this function are unused by the + * capability security module. Returns 0 if this mapping should be allowed + * -EPERM if not. + */ +int cap_file_mmap(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags, + unsigned long addr, unsigned long addr_only) +{ + return cap_mmap_addr(addr); +} diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index fa2341b68331..25c125eaa3d8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3104,7 +3104,7 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot, } /* do DAC check on address space usage */ - rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); + rc = cap_mmap_addr(addr); if (rc || addr_only) return rc; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index d583c0545808..a62197718768 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1199,7 +1199,7 @@ static int smack_file_mmap(struct file *file, int rc; /* do DAC check on address space usage */ - rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); + rc = cap_mmap_addr(addr); if (rc || addr_only) return rc; -- cgit v1.2.2 From e5467859f7f79b69fc49004403009dfdba3bec53 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 May 2012 13:30:51 -0400 Subject: split ->file_mmap() into ->mmap_addr()/->mmap_file() ... i.e. file-dependent and address-dependent checks. Signed-off-by: Al Viro --- security/apparmor/lsm.c | 15 ++++----------- security/capability.c | 3 ++- security/commoncap.c | 21 +++------------------ security/security.c | 12 ++++++++---- security/selinux/hooks.c | 15 ++++++++------- security/smack/smack_lsm.c | 15 +++++---------- 6 files changed, 30 insertions(+), 51 deletions(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 8430d8937afb..8ea39aabe948 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -490,17 +490,9 @@ static int common_mmap(int op, struct file *file, unsigned long prot, return common_file_perm(op, file, mask); } -static int apparmor_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags, - unsigned long addr, unsigned long addr_only) +static int apparmor_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { - int rc = 0; - - /* do DAC check */ - rc = cap_mmap_addr(addr); - if (rc || addr_only) - return rc; - return common_mmap(OP_FMMAP, file, prot, flags); } @@ -646,7 +638,8 @@ static struct security_operations apparmor_ops = { .file_permission = apparmor_file_permission, .file_alloc_security = apparmor_file_alloc_security, .file_free_security = apparmor_file_free_security, - .file_mmap = apparmor_file_mmap, + .mmap_file = apparmor_mmap_file, + .mmap_addr = cap_mmap_addr, .file_mprotect = apparmor_file_mprotect, .file_lock = apparmor_file_lock, diff --git a/security/capability.c b/security/capability.c index fca889676c5e..61095df8b89a 100644 --- a/security/capability.c +++ b/security/capability.c @@ -949,7 +949,8 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, file_alloc_security); set_to_cap_if_null(ops, file_free_security); set_to_cap_if_null(ops, file_ioctl); - set_to_cap_if_null(ops, file_mmap); + set_to_cap_if_null(ops, mmap_addr); + set_to_cap_if_null(ops, mmap_file); set_to_cap_if_null(ops, file_mprotect); set_to_cap_if_null(ops, file_lock); set_to_cap_if_null(ops, file_fcntl); diff --git a/security/commoncap.c b/security/commoncap.c index ebac3618896e..6dbae4650abe 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -980,23 +980,8 @@ int cap_mmap_addr(unsigned long addr) return ret; } -/* - * cap_file_mmap - check if able to map given addr - * @file: unused - * @reqprot: unused - * @prot: unused - * @flags: unused - * @addr: address attempting to be mapped - * @addr_only: unused - * - * If the process is attempting to map memory below dac_mmap_min_addr they need - * CAP_SYS_RAWIO. The other parameters to this function are unused by the - * capability security module. Returns 0 if this mapping should be allowed - * -EPERM if not. - */ -int cap_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags, - unsigned long addr, unsigned long addr_only) +int cap_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { - return cap_mmap_addr(addr); + return 0; } diff --git a/security/security.c b/security/security.c index 5497a57fba01..d91c66d3956b 100644 --- a/security/security.c +++ b/security/security.c @@ -657,18 +657,22 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return security_ops->file_ioctl(file, cmd, arg); } -int security_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags, - unsigned long addr, unsigned long addr_only) +int security_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) { int ret; - ret = security_ops->file_mmap(file, reqprot, prot, flags, addr, addr_only); + ret = security_ops->mmap_file(file, reqprot, prot, flags); if (ret) return ret; return ima_file_mmap(file, prot); } +int security_mmap_addr(unsigned long addr) +{ + return security_ops->mmap_addr(addr); +} + int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot) { diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 25c125eaa3d8..372ec6502aa8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3083,9 +3083,7 @@ error: return rc; } -static int selinux_file_mmap(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags, - unsigned long addr, unsigned long addr_only) +static int selinux_mmap_addr(unsigned long addr) { int rc = 0; u32 sid = current_sid(); @@ -3104,10 +3102,12 @@ static int selinux_file_mmap(struct file *file, unsigned long reqprot, } /* do DAC check on address space usage */ - rc = cap_mmap_addr(addr); - if (rc || addr_only) - return rc; + return cap_mmap_addr(addr); +} +static int selinux_mmap_file(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags) +{ if (selinux_checkreqprot) prot = reqprot; @@ -5570,7 +5570,8 @@ static struct security_operations selinux_ops = { .file_alloc_security = selinux_file_alloc_security, .file_free_security = selinux_file_free_security, .file_ioctl = selinux_file_ioctl, - .file_mmap = selinux_file_mmap, + .mmap_file = selinux_mmap_file, + .mmap_addr = selinux_mmap_addr, .file_mprotect = selinux_file_mprotect, .file_lock = selinux_file_lock, .file_fcntl = selinux_file_fcntl, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index a62197718768..ee0bb5735f35 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1171,7 +1171,7 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, } /** - * smack_file_mmap : + * smack_mmap_file : * Check permissions for a mmap operation. The @file may be NULL, e.g. * if mapping anonymous memory. * @file contains the file structure for file to map (may be NULL). @@ -1180,10 +1180,9 @@ static int smack_file_fcntl(struct file *file, unsigned int cmd, * @flags contains the operational flags. * Return 0 if permission is granted. */ -static int smack_file_mmap(struct file *file, +static int smack_mmap_file(struct file *file, unsigned long reqprot, unsigned long prot, - unsigned long flags, unsigned long addr, - unsigned long addr_only) + unsigned long flags) { struct smack_known *skp; struct smack_rule *srp; @@ -1198,11 +1197,6 @@ static int smack_file_mmap(struct file *file, int tmay; int rc; - /* do DAC check on address space usage */ - rc = cap_mmap_addr(addr); - if (rc || addr_only) - return rc; - if (file == NULL || file->f_dentry == NULL) return 0; @@ -3482,7 +3476,8 @@ struct security_operations smack_ops = { .file_ioctl = smack_file_ioctl, .file_lock = smack_file_lock, .file_fcntl = smack_file_fcntl, - .file_mmap = smack_file_mmap, + .mmap_file = smack_mmap_file, + .mmap_addr = cap_mmap_addr, .file_set_fowner = smack_file_set_fowner, .file_send_sigiotask = smack_file_send_sigiotask, .file_receive = smack_file_receive, -- cgit v1.2.2 From 4f1c28d241d0882f25112d494885cd6084db225b Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 31 May 2012 16:26:02 -0700 Subject: security/keys/keyctl.c: suppress memory allocation failure warning This allocation may be large. The code is probing to see if it will succeed and if not, it falls back to vmalloc(). We should suppress any page-allocation failure messages when the fallback happens. Reported-by: Dave Jones Acked-by: David Howells Cc: James Morris Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/keys/keyctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index ddb3e05bc5fc..18f29de88fda 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -84,7 +84,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, vm = false; if (_payload) { ret = -ENOMEM; - payload = kmalloc(plen, GFP_KERNEL); + payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN); if (!payload) { if (plen <= PAGE_SIZE) goto error2; -- cgit v1.2.2 From 81ab6e7b26b453a795d46f2616ed0e31d97f05b9 Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Thu, 31 May 2012 16:26:15 -0700 Subject: kmod: convert two call sites to call_usermodehelper_fns() Both kernel/sys.c && security/keys/request_key.c where inlining the exact same code as call_usermodehelper_fns(); So simply convert these sites to directly use call_usermodehelper_fns(). Signed-off-by: Boaz Harrosh Cc: Oleg Nesterov Cc: Tetsuo Handa Cc: Ingo Molnar Cc: Peter Zijlstra Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/keys/request_key.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'security') diff --git a/security/keys/request_key.c b/security/keys/request_key.c index cc3790315d2f..000e75017520 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -93,16 +93,9 @@ static void umh_keys_cleanup(struct subprocess_info *info) static int call_usermodehelper_keys(char *path, char **argv, char **envp, struct key *session_keyring, int wait) { - gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL; - struct subprocess_info *info = - call_usermodehelper_setup(path, argv, envp, gfp_mask); - - if (!info) - return -ENOMEM; - - call_usermodehelper_setfns(info, umh_keys_init, umh_keys_cleanup, - key_get(session_keyring)); - return call_usermodehelper_exec(info, wait); + return call_usermodehelper_fns(path, argv, envp, wait, + umh_keys_init, umh_keys_cleanup, + key_get(session_keyring)); } /* -- cgit v1.2.2 From ac34ebb3a67e699edcb5ac72f19d31679369dfaa Mon Sep 17 00:00:00 2001 From: Christopher Yeoh Date: Thu, 31 May 2012 16:26:42 -0700 Subject: aio/vfs: cleanup of rw_copy_check_uvector() and compat_rw_copy_check_uvector() A cleanup of rw_copy_check_uvector and compat_rw_copy_check_uvector after changes made to support CMA in an earlier patch. Rather than having an additional check_access parameter to these functions, the first paramater type is overloaded to allow the caller to specify CHECK_IOVEC_ONLY which means check that the contents of the iovec are valid, but do not check the memory that they point to. This is used by process_vm_readv/writev where we need to validate that a iovec passed to the syscall is valid but do not want to check the memory that it points to at this point because it refers to an address space in another process. Signed-off-by: Chris Yeoh Reviewed-by: Oleg Nesterov Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- security/keys/compat.c | 2 +- security/keys/keyctl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/keys/compat.c b/security/keys/compat.c index fab4f8dda6c6..c92d42b021aa 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -38,7 +38,7 @@ long compat_keyctl_instantiate_key_iov( ret = compat_rw_copy_check_uvector(WRITE, _payload_iov, ioc, ARRAY_SIZE(iovstack), - iovstack, &iov, 1); + iovstack, &iov); if (ret < 0) return ret; if (ret == 0) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 18f29de88fda..21907ea35b15 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1110,7 +1110,7 @@ long keyctl_instantiate_key_iov(key_serial_t id, goto no_payload; ret = rw_copy_check_uvector(WRITE, _payload_iov, ioc, - ARRAY_SIZE(iovstack), iovstack, &iov, 1); + ARRAY_SIZE(iovstack), iovstack, &iov); if (ret < 0) return ret; if (ret == 0) -- cgit v1.2.2 From 8b3ec6814c83d76b85bd13badc48552836c24839 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 May 2012 17:11:23 -0400 Subject: take security_mmap_file() outside of ->mmap_sem Signed-off-by: Al Viro --- security/security.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index d91c66d3956b..3b11b3b72fe2 100644 --- a/security/security.c +++ b/security/security.c @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include #include #define MAX_LSM_EVM_XATTR 2 @@ -657,11 +660,35 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return security_ops->file_ioctl(file, cmd, arg); } -int security_mmap_file(struct file *file, unsigned long reqprot, - unsigned long prot, unsigned long flags) +int security_mmap_file(struct file *file, unsigned long prot, + unsigned long flags) { + unsigned long reqprot = prot; int ret; - + /* + * Does the application expect PROT_READ to imply PROT_EXEC? + * + * (the exception is when the underlying filesystem is noexec + * mounted, in which case we dont add PROT_EXEC.) + */ + if (!(reqprot & PROT_READ)) + goto out; + if (!(current->personality & READ_IMPLIES_EXEC)) + goto out; + if (!file) { + prot |= PROT_EXEC; + } else if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) { +#ifndef CONFIG_MMU + unsigned long caps = 0; + struct address_space *mapping = file->f_mapping; + if (mapping && mapping->backing_dev_info) + caps = mapping->backing_dev_info->capabilities; + if (!(caps & BDI_CAP_EXEC_MAP)) + goto out; +#endif + prot |= PROT_EXEC; + } +out: ret = security_ops->mmap_file(file, reqprot, prot, flags); if (ret) return ret; -- cgit v1.2.2 From 98de59bfe4b2ff6344d9ad8e5296f80de5dcc5b6 Mon Sep 17 00:00:00 2001 From: Al Viro Date: Wed, 30 May 2012 19:58:30 -0400 Subject: take calculation of final prot in security_mmap_file() into a helper Signed-off-by: Al Viro --- security/security.c | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index 3b11b3b72fe2..3efc9b12aef4 100644 --- a/security/security.c +++ b/security/security.c @@ -660,36 +660,46 @@ int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return security_ops->file_ioctl(file, cmd, arg); } -int security_mmap_file(struct file *file, unsigned long prot, - unsigned long flags) +static inline unsigned long mmap_prot(struct file *file, unsigned long prot) { - unsigned long reqprot = prot; - int ret; /* - * Does the application expect PROT_READ to imply PROT_EXEC? - * - * (the exception is when the underlying filesystem is noexec - * mounted, in which case we dont add PROT_EXEC.) + * Does we have PROT_READ and does the application expect + * it to imply PROT_EXEC? If not, nothing to talk about... */ - if (!(reqprot & PROT_READ)) - goto out; + if ((prot & (PROT_READ | PROT_EXEC)) != PROT_READ) + return prot; if (!(current->personality & READ_IMPLIES_EXEC)) - goto out; - if (!file) { - prot |= PROT_EXEC; - } else if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) { + return prot; + /* + * if that's an anonymous mapping, let it. + */ + if (!file) + return prot | PROT_EXEC; + /* + * ditto if it's not on noexec mount, except that on !MMU we need + * BDI_CAP_EXEC_MMAP (== VM_MAYEXEC) in this case + */ + if (!(file->f_path.mnt->mnt_flags & MNT_NOEXEC)) { #ifndef CONFIG_MMU unsigned long caps = 0; struct address_space *mapping = file->f_mapping; if (mapping && mapping->backing_dev_info) caps = mapping->backing_dev_info->capabilities; if (!(caps & BDI_CAP_EXEC_MAP)) - goto out; + return prot; #endif - prot |= PROT_EXEC; + return prot | PROT_EXEC; } -out: - ret = security_ops->mmap_file(file, reqprot, prot, flags); + /* anything on noexec mount won't get PROT_EXEC */ + return prot; +} + +int security_mmap_file(struct file *file, unsigned long prot, + unsigned long flags) +{ + int ret; + ret = security_ops->mmap_file(file, prot, + mmap_prot(file, prot), flags); if (ret) return ret; return ima_file_mmap(file, prot); -- cgit v1.2.2