Skip to content

Commit

Permalink
LSM: Switch to lists of hooks
Browse files Browse the repository at this point in the history
Instead of using a vector of security operations
with explicit, special case stacking of the capability
and yama hooks use lists of hooks with capability and
yama hooks included as appropriate.

The security_operations structure is no longer required.
Instead, there is a union of the function pointers that
allows all the hooks lists to use a common mechanism for
list management while retaining typing. Each module
supplies an array describing the hooks it provides instead
of a sparsely populated security_operations structure.
The description includes the element that gets put on
the hook list, avoiding the issues surrounding individual
element allocation.

The method for registering security modules is changed to
reflect the information available. The method for removing
a module, currently only used by SELinux, has also changed.
It should be generic now, however if there are potential
race conditions based on ordering of hook removal that needs
to be addressed by the calling module.

The security hooks are called from the lists and the first
failure is returned.

Signed-off-by: Casey Schaufler <[email protected]>
Acked-by: John Johansen <[email protected]>
Acked-by: Kees Cook <[email protected]>
Acked-by: Paul Moore <[email protected]>
Acked-by:  Stephen Smalley <[email protected]>
Acked-by: Tetsuo Handa <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
cschaufler authored and James Morris committed May 12, 2015
1 parent e20b043 commit b1d9e6b
Show file tree
Hide file tree
Showing 13 changed files with 627 additions and 387 deletions.
77 changes: 53 additions & 24 deletions include/linux/lsm_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,10 @@
#define __LINUX_LSM_HOOKS_H

#include <linux/security.h>

/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10

#ifdef CONFIG_SECURITY
#include <linux/init.h>
#include <linux/rculist.h>

/**
* struct security_operations - main security structure
*
* Security module identifier.
*
* @name:
* A string that acts as a unique identifier for the LSM with max number
* of characters = SECURITY_NAME_MAX.
*
* Security hooks for program execution operations.
*
* @bprm_set_creds:
Expand Down Expand Up @@ -1310,9 +1299,7 @@
* This is the main security structure.
*/

struct security_operations {
char name[SECURITY_NAME_MAX + 1];

union security_list_options {
int (*binder_set_context_mgr)(struct task_struct *mgr);
int (*binder_transaction)(struct task_struct *from,
struct task_struct *to);
Expand Down Expand Up @@ -1837,21 +1824,63 @@ struct security_hook_heads {
#endif /* CONFIG_AUDIT */
};

/*
* Security module hook list structure.
* For use with generic list macros for common operations.
*/
struct security_hook_list {
struct list_head list;
struct list_head *head;
union security_list_options hook;
};

/*
* Initializing a security_hook_list structure takes
* up a lot of space in a source file. This macro takes
* care of the common case and reduces the amount of
* text involved.
* Casey says: Comment is true in the next patch.
*/
#define LSM_HOOK_INIT(HEAD, HOOK) .HEAD = HOOK
#define LSM_HOOK_INIT(HEAD, HOOK) \
{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }

extern struct security_hook_heads security_hook_heads;

static inline void security_add_hooks(struct security_hook_list *hooks,
int count)
{
int i;

/* prototypes */
extern int security_module_enable(struct security_operations *ops);
extern int register_security(struct security_operations *ops);
extern void __init security_fixup_ops(struct security_operations *ops);
extern void reset_security_ops(void);
for (i = 0; i < count; i++)
list_add_tail_rcu(&hooks[i].list, hooks[i].head);
}

#endif /* CONFIG_SECURITY */
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
/*
* Assuring the safety of deleting a security module is up to
* the security module involved. This may entail ordering the
* module's hook list in a particular way, refusing to disable
* the module once a policy is loaded or any number of other
* actions better imagined than described.
*
* The name of the configuration option reflects the only module
* that currently uses the mechanism. Any developer who thinks
* disabling their module is a good idea needs to be at least as
* careful as the SELinux team.
*/
static inline void security_delete_hooks(struct security_hook_list *hooks,
int count)
{
int i;

for (i = 0; i < count; i++)
list_del_rcu(&hooks[i].list);
}
#endif /* CONFIG_SECURITY_SELINUX_DISABLE */

extern int __init security_module_enable(const char *module);
extern void __init capability_add_hooks(void);
#ifdef CONFIG_SECURITY_YAMA_STACKED
void __init yama_add_hooks(void);
#endif

#endif /* ! __LINUX_LSM_HOOKS_H */
46 changes: 4 additions & 42 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/mm.h>

struct linux_binprm;
struct cred;
Expand Down Expand Up @@ -54,9 +55,6 @@ struct xattr;
struct xfrm_sec_ctx;
struct mm_struct;

/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10

/* If capable should audit the security request */
#define SECURITY_CAP_NOAUDIT 0
#define SECURITY_CAP_AUDIT 1
Expand All @@ -69,10 +67,7 @@ struct audit_krule;
struct user_namespace;
struct timezone;

/*
* These functions are in security/capability.c and are used
* as the default capabilities functions
*/
/* These functions are in security/commoncap.c */
extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit);
extern int cap_settime(const struct timespec *ts, const struct timezone *tz);
Expand Down Expand Up @@ -114,8 +109,6 @@ struct xfrm_state;
struct xfrm_user_sec_ctx;
struct seq_file;

extern int cap_netlink_send(struct sock *sk, struct sk_buff *skb);

#ifdef CONFIG_MMU
extern unsigned long mmap_min_addr;
extern unsigned long dac_mmap_min_addr;
Expand Down Expand Up @@ -472,7 +465,7 @@ static inline int security_settime(const struct timespec *ts,

static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
return cap_vm_enough_memory(mm, pages);
return __vm_enough_memory(mm, pages, cap_vm_enough_memory(mm, pages));
}

static inline int security_bprm_set_creds(struct linux_binprm *bprm)
Expand Down Expand Up @@ -1075,7 +1068,7 @@ static inline int security_setprocattr(struct task_struct *p, char *name, void *

static inline int security_netlink_send(struct sock *sk, struct sk_buff *skb)
{
return cap_netlink_send(sk, skb);
return 0;
}

static inline int security_ismaclabel(const char *name)
Expand Down Expand Up @@ -1643,36 +1636,5 @@ static inline void free_secdata(void *secdata)
{ }
#endif /* CONFIG_SECURITY */

#ifdef CONFIG_SECURITY_YAMA
extern int yama_ptrace_access_check(struct task_struct *child,
unsigned int mode);
extern int yama_ptrace_traceme(struct task_struct *parent);
extern void yama_task_free(struct task_struct *task);
extern int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5);
#else
static inline int yama_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
return 0;
}

static inline int yama_ptrace_traceme(struct task_struct *parent)
{
return 0;
}

static inline void yama_task_free(struct task_struct *task)
{
}

static inline int yama_task_prctl(int option, unsigned long arg2,
unsigned long arg3, unsigned long arg4,
unsigned long arg5)
{
return -ENOSYS;
}
#endif /* CONFIG_SECURITY_YAMA */

#endif /* ! __LINUX_SECURITY_H */

2 changes: 1 addition & 1 deletion security/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ obj-y += commoncap.o
obj-$(CONFIG_MMU) += min_addr.o

# Object file lists
obj-$(CONFIG_SECURITY) += security.o capability.o
obj-$(CONFIG_SECURITY) += security.o
obj-$(CONFIG_SECURITYFS) += inode.o
obj-$(CONFIG_SECURITY_SELINUX) += selinux/
obj-$(CONFIG_SECURITY_SMACK) += smack/
Expand Down
12 changes: 4 additions & 8 deletions security/apparmor/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,9 +347,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
file_inode(bprm->file)->i_mode
};
const char *name = NULL, *target = NULL, *info = NULL;
int error = cap_bprm_set_creds(bprm);
if (error)
return error;
int error = 0;

if (bprm->cred_prepared)
return 0;
Expand Down Expand Up @@ -531,15 +529,13 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
*/
int apparmor_bprm_secureexec(struct linux_binprm *bprm)
{
int ret = cap_bprm_secureexec(bprm);

/* the decision to use secure exec is computed in set_creds
* and stored in bprm->unsafe.
*/
if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED))
ret = 1;
if (bprm->unsafe & AA_SECURE_X_NEEDED)
return 1;

return ret;
return 0;
}

/**
Expand Down
51 changes: 14 additions & 37 deletions security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,11 @@ static void apparmor_cred_transfer(struct cred *new, const struct cred *old)
static int apparmor_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
int error = cap_ptrace_access_check(child, mode);
if (error)
return error;

return aa_ptrace(current, child, mode);
}

static int apparmor_ptrace_traceme(struct task_struct *parent)
{
int error = cap_ptrace_traceme(parent);
if (error)
return error;

return aa_ptrace(parent, current, PTRACE_MODE_ATTACH);
}

Expand All @@ -123,10 +115,10 @@ static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective,
cred = __task_cred(target);
profile = aa_cred_profile(cred);

*effective = cred->cap_effective;
*inheritable = cred->cap_inheritable;
*permitted = cred->cap_permitted;

/*
* cap_capget is stacked ahead of this and will
* initialize effective and permitted.
*/
if (!unconfined(profile) && !COMPLAIN_MODE(profile)) {
*effective = cap_intersect(*effective, profile->caps.allow);
*permitted = cap_intersect(*permitted, profile->caps.allow);
Expand All @@ -140,13 +132,11 @@ static int apparmor_capable(const struct cred *cred, struct user_namespace *ns,
int cap, int audit)
{
struct aa_profile *profile;
/* cap_capable returns 0 on success, else -EPERM */
int error = cap_capable(cred, ns, cap, audit);
if (!error) {
profile = aa_cred_profile(cred);
if (!unconfined(profile))
error = aa_capable(profile, cap, audit);
}
int error = 0;

profile = aa_cred_profile(cred);
if (!unconfined(profile))
error = aa_capable(profile, cap, audit);
return error;
}

Expand Down Expand Up @@ -615,9 +605,7 @@ static int apparmor_task_setrlimit(struct task_struct *task,
return error;
}

static struct security_operations apparmor_ops = {
LSM_HOOK_INIT(name, "apparmor"),

static struct security_hook_list apparmor_hooks[] = {
LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
LSM_HOOK_INIT(capget, apparmor_capget),
Expand All @@ -640,7 +628,6 @@ static struct security_operations apparmor_ops = {
LSM_HOOK_INIT(file_alloc_security, apparmor_file_alloc_security),
LSM_HOOK_INIT(file_free_security, apparmor_file_free_security),
LSM_HOOK_INIT(mmap_file, apparmor_mmap_file),
LSM_HOOK_INIT(mmap_addr, cap_mmap_addr),
LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect),
LSM_HOOK_INIT(file_lock, apparmor_file_lock),

Expand Down Expand Up @@ -898,7 +885,7 @@ static int __init apparmor_init(void)
{
int error;

if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) {
if (!apparmor_enabled || !security_module_enable("apparmor")) {
aa_info_message("AppArmor disabled by boot time parameter");
apparmor_enabled = 0;
return 0;
Expand All @@ -913,17 +900,10 @@ static int __init apparmor_init(void)
error = set_init_cxt();
if (error) {
AA_ERROR("Failed to set context on init task\n");
goto register_security_out;
}

error = register_security(&apparmor_ops);
if (error) {
struct cred *cred = (struct cred *)current->real_cred;
aa_free_task_context(cred_cxt(cred));
cred_cxt(cred) = NULL;
AA_ERROR("Unable to register AppArmor\n");
goto register_security_out;
aa_free_root_ns();
goto alloc_out;
}
security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks));

/* Report that AppArmor successfully initialized */
apparmor_initialized = 1;
Expand All @@ -936,9 +916,6 @@ static int __init apparmor_init(void)

return error;

register_security_out:
aa_free_root_ns();

alloc_out:
aa_destroy_aafs();

Expand Down
Loading

0 comments on commit b1d9e6b

Please sign in to comment.