diff options
Diffstat (limited to 'security/apparmor/include')
-rw-r--r-- | security/apparmor/include/apparmor.h | 6 | ||||
-rw-r--r-- | security/apparmor/include/apparmorfs.h | 67 | ||||
-rw-r--r-- | security/apparmor/include/audit.h | 17 | ||||
-rw-r--r-- | security/apparmor/include/capability.h | 8 | ||||
-rw-r--r-- | security/apparmor/include/context.h | 201 | ||||
-rw-r--r-- | security/apparmor/include/domain.h | 13 | ||||
-rw-r--r-- | security/apparmor/include/file.h | 114 | ||||
-rw-r--r-- | security/apparmor/include/ipc.h | 16 | ||||
-rw-r--r-- | security/apparmor/include/label.h | 441 | ||||
-rw-r--r-- | security/apparmor/include/lib.h | 120 | ||||
-rw-r--r-- | security/apparmor/include/path.h | 7 | ||||
-rw-r--r-- | security/apparmor/include/perms.h | 155 | ||||
-rw-r--r-- | security/apparmor/include/policy.h | 131 | ||||
-rw-r--r-- | security/apparmor/include/policy_ns.h | 21 | ||||
-rw-r--r-- | security/apparmor/include/policy_unpack.h | 68 | ||||
-rw-r--r-- | security/apparmor/include/procattr.h | 8 | ||||
-rw-r--r-- | security/apparmor/include/resource.h | 6 |
17 files changed, 1125 insertions, 274 deletions
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h index 1750cc0721c1..aaf893f4e4f5 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -4,7 +4,7 @@ * This file contains AppArmor basic global * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -27,8 +27,10 @@ #define AA_CLASS_NET 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 +#define AA_CLASS_PTRACE 9 +#define AA_CLASS_LABEL 16 -#define AA_CLASS_LAST AA_CLASS_DOMAIN +#define AA_CLASS_LAST AA_CLASS_LABEL /* Control parameters settable through module/boot flags */ extern enum audit_mode aa_g_audit; diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h index 120a798b5bb0..bd689114bf93 100644 --- a/security/apparmor/include/apparmorfs.h +++ b/security/apparmor/include/apparmorfs.h @@ -17,49 +17,49 @@ extern struct path aa_null; -enum aa_fs_type { - AA_FS_TYPE_BOOLEAN, - AA_FS_TYPE_STRING, - AA_FS_TYPE_U64, - AA_FS_TYPE_FOPS, - AA_FS_TYPE_DIR, +enum aa_sfs_type { + AA_SFS_TYPE_BOOLEAN, + AA_SFS_TYPE_STRING, + AA_SFS_TYPE_U64, + AA_SFS_TYPE_FOPS, + AA_SFS_TYPE_DIR, }; -struct aa_fs_entry; +struct aa_sfs_entry; -struct aa_fs_entry { +struct aa_sfs_entry { const char *name; struct dentry *dentry; umode_t mode; - enum aa_fs_type v_type; + enum aa_sfs_type v_type; union { bool boolean; char *string; unsigned long u64; - struct aa_fs_entry *files; + struct aa_sfs_entry *files; } v; const struct file_operations *file_ops; }; -extern const struct file_operations aa_fs_seq_file_ops; +extern const struct file_operations aa_sfs_seq_file_ops; -#define AA_FS_FILE_BOOLEAN(_name, _value) \ +#define AA_SFS_FILE_BOOLEAN(_name, _value) \ { .name = (_name), .mode = 0444, \ - .v_type = AA_FS_TYPE_BOOLEAN, .v.boolean = (_value), \ - .file_ops = &aa_fs_seq_file_ops } -#define AA_FS_FILE_STRING(_name, _value) \ + .v_type = AA_SFS_TYPE_BOOLEAN, .v.boolean = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_STRING(_name, _value) \ { .name = (_name), .mode = 0444, \ - .v_type = AA_FS_TYPE_STRING, .v.string = (_value), \ - .file_ops = &aa_fs_seq_file_ops } -#define AA_FS_FILE_U64(_name, _value) \ + .v_type = AA_SFS_TYPE_STRING, .v.string = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_U64(_name, _value) \ { .name = (_name), .mode = 0444, \ - .v_type = AA_FS_TYPE_U64, .v.u64 = (_value), \ - .file_ops = &aa_fs_seq_file_ops } -#define AA_FS_FILE_FOPS(_name, _mode, _fops) \ - { .name = (_name), .v_type = AA_FS_TYPE_FOPS, \ + .v_type = AA_SFS_TYPE_U64, .v.u64 = (_value), \ + .file_ops = &aa_sfs_seq_file_ops } +#define AA_SFS_FILE_FOPS(_name, _mode, _fops) \ + { .name = (_name), .v_type = AA_SFS_TYPE_FOPS, \ .mode = (_mode), .file_ops = (_fops) } -#define AA_FS_DIR(_name, _value) \ - { .name = (_name), .v_type = AA_FS_TYPE_DIR, .v.files = (_value) } +#define AA_SFS_DIR(_name, _value) \ + { .name = (_name), .v_type = AA_SFS_TYPE_DIR, .v.files = (_value) } extern void __init aa_destroy_aafs(void); @@ -74,6 +74,7 @@ enum aafs_ns_type { AAFS_NS_LOAD, AAFS_NS_REPLACE, AAFS_NS_REMOVE, + AAFS_NS_REVISION, AAFS_NS_COUNT, AAFS_NS_MAX_COUNT, AAFS_NS_SIZE, @@ -102,16 +103,22 @@ enum aafs_prof_type { #define ns_subload(X) ((X)->dents[AAFS_NS_LOAD]) #define ns_subreplace(X) ((X)->dents[AAFS_NS_REPLACE]) #define ns_subremove(X) ((X)->dents[AAFS_NS_REMOVE]) +#define ns_subrevision(X) ((X)->dents[AAFS_NS_REVISION]) #define prof_dir(X) ((X)->dents[AAFS_PROF_DIR]) #define prof_child_dir(X) ((X)->dents[AAFS_PROF_PROFS]) -void __aa_fs_profile_rmdir(struct aa_profile *profile); -void __aa_fs_profile_migrate_dents(struct aa_profile *old, +void __aa_bump_ns_revision(struct aa_ns *ns); +void __aafs_profile_rmdir(struct aa_profile *profile); +void __aafs_profile_migrate_dents(struct aa_profile *old, struct aa_profile *new); -int __aa_fs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); -void __aa_fs_ns_rmdir(struct aa_ns *ns); -int __aa_fs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, - const char *name); +int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent); +void __aafs_ns_rmdir(struct aa_ns *ns); +int __aafs_ns_mkdir(struct aa_ns *ns, struct dentry *parent, const char *name, + struct dentry *dent); + +struct aa_loaddata; +void __aa_fs_remove_rawdata(struct aa_loaddata *rawdata); +int __aa_fs_create_rawdata(struct aa_ns *ns, struct aa_loaddata *rawdata); #endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h index fdc4774318ba..c68839a44351 100644 --- a/security/apparmor/include/audit.h +++ b/security/apparmor/include/audit.h @@ -22,8 +22,7 @@ #include <linux/slab.h> #include "file.h" - -struct aa_profile; +#include "label.h" extern const char *const audit_mode_names[]; #define AUDIT_MAX_INDEX 5 @@ -65,10 +64,12 @@ enum audit_type { #define OP_GETATTR "getattr" #define OP_OPEN "open" +#define OP_FRECEIVE "file_receive" #define OP_FPERM "file_perm" #define OP_FLOCK "file_lock" #define OP_FMMAP "file_mmap" #define OP_FMPROT "file_mprotect" +#define OP_INHERIT "file_inherit" #define OP_CREATE "create" #define OP_POST_CREATE "post_create" @@ -91,6 +92,8 @@ enum audit_type { #define OP_CHANGE_HAT "change_hat" #define OP_CHANGE_PROFILE "change_profile" #define OP_CHANGE_ONEXEC "change_onexec" +#define OP_STACK "stack" +#define OP_STACK_ONEXEC "stack_onexec" #define OP_SETPROCATTR "setprocattr" #define OP_SETRLIMIT "setrlimit" @@ -102,19 +105,19 @@ enum audit_type { struct apparmor_audit_data { int error; - const char *op; int type; - void *profile; + const char *op; + struct aa_label *label; const char *name; const char *info; + u32 request; + u32 denied; union { /* these entries require a custom callback fn */ struct { - struct aa_profile *peer; + struct aa_label *peer; struct { const char *target; - u32 request; - u32 denied; kuid_t ouid; } fs; }; diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h index fc3fa381d850..e0304e2aeb7f 100644 --- a/security/apparmor/include/capability.h +++ b/security/apparmor/include/capability.h @@ -19,11 +19,12 @@ #include "apparmorfs.h" -struct aa_profile; +struct aa_label; /* aa_caps - confinement data for capabilities * @allowed: capabilities mask * @audit: caps that are to be audited + * @denied: caps that are explicitly denied * @quiet: caps that should not be audited * @kill: caps that when requested will result in the task being killed * @extended: caps that are subject finer grained mediation @@ -31,14 +32,15 @@ struct aa_profile; struct aa_caps { kernel_cap_t allow; kernel_cap_t audit; + kernel_cap_t denied; kernel_cap_t quiet; kernel_cap_t kill; kernel_cap_t extended; }; -extern struct aa_fs_entry aa_fs_entry_caps[]; +extern struct aa_sfs_entry aa_sfs_entry_caps[]; -int aa_capable(struct aa_profile *profile, int cap, int audit); +int aa_capable(struct aa_label *label, int cap, int audit); static inline void aa_free_cap_rules(struct aa_caps *caps) { diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h index 5b18fedab4c8..6ae07e9aaa17 100644 --- a/security/apparmor/include/context.h +++ b/security/apparmor/include/context.h @@ -19,60 +19,28 @@ #include <linux/slab.h> #include <linux/sched.h> -#include "policy.h" +#include "label.h" #include "policy_ns.h" #define cred_ctx(X) ((X)->security) #define current_ctx() cred_ctx(current_cred()) -/* struct aa_file_ctx - the AppArmor context the file was opened in - * @perms: the permission the file was opened with - * - * The file_ctx could currently be directly stored in file->f_security - * as the profile reference is now stored in the f_cred. However the - * ctx struct will expand in the future so we keep the struct. - */ -struct aa_file_ctx { - u16 allow; -}; - -/** - * aa_alloc_file_context - allocate file_ctx - * @gfp: gfp flags for allocation - * - * Returns: file_ctx or NULL on failure - */ -static inline struct aa_file_ctx *aa_alloc_file_context(gfp_t gfp) -{ - return kzalloc(sizeof(struct aa_file_ctx), gfp); -} - -/** - * aa_free_file_context - free a file_ctx - * @ctx: file_ctx to free (MAYBE_NULL) - */ -static inline void aa_free_file_context(struct aa_file_ctx *ctx) -{ - if (ctx) - kzfree(ctx); -} - /** * struct aa_task_ctx - primary label for confined tasks - * @profile: the current profile (NOT NULL) - * @exec: profile to transition to on next exec (MAYBE NULL) - * @previous: profile the task may return to (MAYBE NULL) - * @token: magic value the task must know for returning to @previous_profile + * @label: the current label (NOT NULL) + * @exec: label to transition to on next exec (MAYBE NULL) + * @previous: label the task may return to (MAYBE NULL) + * @token: magic value the task must know for returning to @previous * - * Contains the task's current profile (which could change due to + * Contains the task's current label (which could change due to * change_hat). Plus the hat_magic needed during change_hat. * * TODO: make so a task can be confined by a stack of contexts */ struct aa_task_ctx { - struct aa_profile *profile; - struct aa_profile *onexec; - struct aa_profile *previous; + struct aa_label *label; + struct aa_label *onexec; + struct aa_label *previous; u64 token; }; @@ -80,40 +48,51 @@ struct aa_task_ctx *aa_alloc_task_context(gfp_t flags); void aa_free_task_context(struct aa_task_ctx *ctx); void aa_dup_task_context(struct aa_task_ctx *new, const struct aa_task_ctx *old); -int aa_replace_current_profile(struct aa_profile *profile); -int aa_set_current_onexec(struct aa_profile *profile); -int aa_set_current_hat(struct aa_profile *profile, u64 token); -int aa_restore_previous_profile(u64 cookie); -struct aa_profile *aa_get_task_profile(struct task_struct *task); +int aa_replace_current_label(struct aa_label *label); +int aa_set_current_onexec(struct aa_label *label, bool stack); +int aa_set_current_hat(struct aa_label *label, u64 token); +int aa_restore_previous_label(u64 cookie); +struct aa_label *aa_get_task_label(struct task_struct *task); /** - * aa_cred_profile - obtain cred's profiles - * @cred: cred to obtain profiles from (NOT NULL) + * aa_cred_raw_label - obtain cred's label + * @cred: cred to obtain label from (NOT NULL) * - * Returns: confining profile + * Returns: confining label * * does NOT increment reference count */ -static inline struct aa_profile *aa_cred_profile(const struct cred *cred) +static inline struct aa_label *aa_cred_raw_label(const struct cred *cred) { struct aa_task_ctx *ctx = cred_ctx(cred); - AA_BUG(!ctx || !ctx->profile); - return ctx->profile; + AA_BUG(!ctx || !ctx->label); + return ctx->label; +} + +/** + * aa_get_newest_cred_label - obtain the newest label on a cred + * @cred: cred to obtain label from (NOT NULL) + * + * Returns: newest version of confining label + */ +static inline struct aa_label *aa_get_newest_cred_label(const struct cred *cred) +{ + return aa_get_newest_label(aa_cred_raw_label(cred)); } /** - * __aa_task_profile - retrieve another task's profile + * __aa_task_raw_label - retrieve another task's label * @task: task to query (NOT NULL) * - * Returns: @task's profile without incrementing its ref count + * Returns: @task's label without incrementing its ref count * * If @task != current needs to be called in RCU safe critical section */ -static inline struct aa_profile *__aa_task_profile(struct task_struct *task) +static inline struct aa_label *__aa_task_raw_label(struct task_struct *task) { - return aa_cred_profile(__task_cred(task)); + return aa_cred_raw_label(__task_cred(task)); } /** @@ -124,50 +103,114 @@ static inline struct aa_profile *__aa_task_profile(struct task_struct *task) */ static inline bool __aa_task_is_confined(struct task_struct *task) { - return !unconfined(__aa_task_profile(task)); + return !unconfined(__aa_task_raw_label(task)); } /** - * __aa_current_profile - find the current tasks confining profile + * aa_current_raw_label - find the current tasks confining label * - * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) + * Returns: up to date confining label or the ns unconfined label (NOT NULL) * * This fn will not update the tasks cred to the most up to date version - * of the profile so it is safe to call when inside of locks. + * of the label so it is safe to call when inside of locks. */ -static inline struct aa_profile *__aa_current_profile(void) +static inline struct aa_label *aa_current_raw_label(void) { - return aa_cred_profile(current_cred()); + return aa_cred_raw_label(current_cred()); } /** - * aa_current_profile - find the current tasks confining profile and do updates + * aa_get_current_label - get the newest version of the current tasks label + * + * Returns: newest version of confining label (NOT NULL) * - * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) + * This fn will not update the tasks cred, so it is safe inside of locks * - * This fn will update the tasks cred structure if the profile has been - * replaced. Not safe to call inside locks + * The returned reference must be put with aa_put_label() */ -static inline struct aa_profile *aa_current_profile(void) +static inline struct aa_label *aa_get_current_label(void) { - const struct aa_task_ctx *ctx = current_ctx(); - struct aa_profile *profile; + struct aa_label *l = aa_current_raw_label(); - AA_BUG(!ctx || !ctx->profile); + if (label_is_stale(l)) + return aa_get_newest_label(l); + return aa_get_label(l); +} + +#define __end_current_label_crit_section(X) end_current_label_crit_section(X) - if (profile_is_stale(ctx->profile)) { - profile = aa_get_newest_profile(ctx->profile); - aa_replace_current_profile(profile); - aa_put_profile(profile); - ctx = current_ctx(); +/** + * end_label_crit_section - put a reference found with begin_current_label.. + * @label: label reference to put + * + * Should only be used with a reference obtained with + * begin_current_label_crit_section and never used in situations where the + * task cred may be updated + */ +static inline void end_current_label_crit_section(struct aa_label *label) +{ + if (label != aa_current_raw_label()) + aa_put_label(label); +} + +/** + * __begin_current_label_crit_section - current's confining label + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * safe to call inside locks + * + * The returned reference must be put with __end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between __begin_current_label_crit_section() .. + * __end_current_label_crit_section() + */ +static inline struct aa_label *__begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + if (label_is_stale(label)) + label = aa_get_newest_label(label); + + return label; +} + +/** + * begin_current_label_crit_section - current's confining label and update it + * + * Returns: up to date confining label or the ns unconfined label (NOT NULL) + * + * Not safe to call inside locks + * + * The returned reference must be put with end_current_label_crit_section() + * This must NOT be used if the task cred could be updated within the + * critical section between begin_current_label_crit_section() .. + * end_current_label_crit_section() + */ +static inline struct aa_label *begin_current_label_crit_section(void) +{ + struct aa_label *label = aa_current_raw_label(); + + if (label_is_stale(label)) { + label = aa_get_newest_label(label); + if (aa_replace_current_label(label) == 0) + /* task cred will keep the reference */ + aa_put_label(label); } - return ctx->profile; + return label; } static inline struct aa_ns *aa_get_current_ns(void) { - return aa_get_ns(__aa_current_profile()->ns); + struct aa_label *label; + struct aa_ns *ns; + + label = __begin_current_label_crit_section(); + ns = aa_get_ns(labels_ns(label)); + __end_current_label_crit_section(label); + + return ns; } /** @@ -176,8 +219,8 @@ static inline struct aa_ns *aa_get_current_ns(void) */ static inline void aa_clear_task_ctx_trans(struct aa_task_ctx *ctx) { - aa_put_profile(ctx->previous); - aa_put_profile(ctx->onexec); + aa_put_label(ctx->previous); + aa_put_label(ctx->onexec); ctx->previous = NULL; ctx->onexec = NULL; ctx->token = 0; diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h index 30544729878a..bab5810b6e9a 100644 --- a/security/apparmor/include/domain.h +++ b/security/apparmor/include/domain.h @@ -23,14 +23,17 @@ struct aa_domain { char **table; }; +#define AA_CHANGE_NOFLAGS 0 +#define AA_CHANGE_TEST 1 +#define AA_CHANGE_CHILD 2 +#define AA_CHANGE_ONEXEC 4 +#define AA_CHANGE_STACK 8 + int apparmor_bprm_set_creds(struct linux_binprm *bprm); int apparmor_bprm_secureexec(struct linux_binprm *bprm); -void apparmor_bprm_committing_creds(struct linux_binprm *bprm); -void apparmor_bprm_committed_creds(struct linux_binprm *bprm); void aa_free_domain_entries(struct aa_domain *domain); -int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); -int aa_change_profile(const char *fqname, bool onexec, bool permtest, - bool stack); +int aa_change_hat(const char *hats[], int count, u64 token, int flags); +int aa_change_profile(const char *fqname, int flags); #endif /* __AA_DOMAIN_H */ diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h index 38f821bf49b6..001e40073ff9 100644 --- a/security/apparmor/include/file.h +++ b/security/apparmor/include/file.h @@ -15,38 +15,73 @@ #ifndef __AA_FILE_H #define __AA_FILE_H +#include <linux/spinlock.h> + #include "domain.h" #include "match.h" +#include "perms.h" struct aa_profile; struct path; -/* - * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags - * for profile permissions - */ -#define AA_MAY_CREATE 0x0010 -#define AA_MAY_DELETE 0x0020 -#define AA_MAY_META_WRITE 0x0040 -#define AA_MAY_META_READ 0x0080 - -#define AA_MAY_CHMOD 0x0100 -#define AA_MAY_CHOWN 0x0200 -#define AA_MAY_LOCK 0x0400 -#define AA_EXEC_MMAP 0x0800 - -#define AA_MAY_LINK 0x1000 -#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ -#define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ -#define AA_MAY_CHANGE_PROFILE 0x80000000 -#define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ +#define mask_mode_t(X) (X & (MAY_EXEC | MAY_WRITE | MAY_READ | MAY_APPEND)) #define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ AA_MAY_CREATE | AA_MAY_DELETE | \ - AA_MAY_META_READ | AA_MAY_META_WRITE | \ + AA_MAY_GETATTR | AA_MAY_SETATTR | \ AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ AA_EXEC_MMAP | AA_MAY_LINK) +#define file_ctx(X) ((struct aa_file_ctx *)(X)->f_security) + +/* struct aa_file_ctx - the AppArmor context the file was opened in + * @lock: lock to update the ctx + * @label: label currently cached on the ctx + * @perms: the permission the file was opened with + */ +struct aa_file_ctx { + spinlock_t lock; + struct aa_label __rcu *label; + u32 allow; +}; + +/** + * aa_alloc_file_ctx - allocate file_ctx + * @label: initial label of task creating the file + * @gfp: gfp flags for allocation + * + * Returns: file_ctx or NULL on failure + */ +static inline struct aa_file_ctx *aa_alloc_file_ctx(struct aa_label *label, + gfp_t gfp) +{ + struct aa_file_ctx *ctx; + + ctx = kzalloc(sizeof(struct aa_file_ctx), gfp); + if (ctx) { + spin_lock_init(&ctx->lock); + rcu_assign_pointer(ctx->label, aa_get_label(label)); + } + return ctx; +} + +/** + * aa_free_file_ctx - free a file_ctx + * @ctx: file_ctx to free (MAYBE_NULL) + */ +static inline void aa_free_file_ctx(struct aa_file_ctx *ctx) +{ + if (ctx) { + aa_put_label(rcu_access_pointer(ctx->label)); + kzfree(ctx); + } +} + +static inline struct aa_label *aa_get_file_label(struct aa_file_ctx *ctx) +{ + return aa_get_label_rcu(&ctx->label); +} + /* * The xindex is broken into 3 parts * - index - an index into either the exec name table or the variable table @@ -75,25 +110,6 @@ struct path_cond { umode_t mode; }; -/* struct file_perms - file permission - * @allow: mask of permissions that are allowed - * @audit: mask of permissions to force an audit message for - * @quiet: mask of permissions to quiet audit messages for - * @kill: mask of permissions that when matched will kill the task - * @xindex: exec transition index if @allow contains MAY_EXEC - * - * The @audit and @queit mask should be mutually exclusive. - */ -struct file_perms { - u32 allow; - u32 audit; - u32 quiet; - u32 kill; - u16 xindex; -}; - -extern struct file_perms nullperms; - #define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) /* FIXME: split perms from dfa and match this to description @@ -144,9 +160,10 @@ static inline u16 dfa_map_xindex(u16 mask) #define dfa_other_xindex(dfa, state) \ dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) -int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, +int aa_audit_file(struct aa_profile *profile, struct aa_perms *perms, const char *op, u32 request, const char *name, - const char *target, kuid_t ouid, const char *info, int error); + const char *target, struct aa_label *tlabel, kuid_t ouid, + const char *info, int error); /** * struct aa_file_rules - components used for file rule permissions @@ -167,20 +184,27 @@ struct aa_file_rules { /* TODO: add delegate table */ }; +struct aa_perms aa_compute_fperms(struct aa_dfa *dfa, unsigned int state, + struct path_cond *cond); unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, const char *name, struct path_cond *cond, - struct file_perms *perms); + struct aa_perms *perms); -int aa_path_perm(const char *op, struct aa_profile *profile, +int __aa_path_perm(const char *op, struct aa_profile *profile, + const char *name, u32 request, struct path_cond *cond, + int flags, struct aa_perms *perms); +int aa_path_perm(const char *op, struct aa_label *label, const struct path *path, int flags, u32 request, struct path_cond *cond); -int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, +int aa_path_link(struct aa_label *label, struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry); -int aa_file_perm(const char *op, struct aa_profile *profile, struct file *file, +int aa_file_perm(const char *op, struct aa_label *label, struct file *file, u32 request); +void aa_inherit_files(const struct cred *cred, struct files_struct *files); + static inline void aa_free_file_rules(struct aa_file_rules *rules) { aa_put_dfa(rules->dfa); diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h index 288ca76e2fb1..656fdb81c8a0 100644 --- a/security/apparmor/include/ipc.h +++ b/security/apparmor/include/ipc.h @@ -4,7 +4,7 @@ * This file contains AppArmor ipc mediation function definitions. * * Copyright (C) 1998-2008 Novell/SUSE - * Copyright 2009-2010 Canonical Ltd. + * Copyright 2009-2017 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -19,10 +19,16 @@ struct aa_profile; -int aa_may_ptrace(struct aa_profile *tracer, struct aa_profile *tracee, - unsigned int mode); +#define AA_PTRACE_TRACE MAY_WRITE +#define AA_PTRACE_READ MAY_READ +#define AA_MAY_BE_TRACED AA_MAY_APPEND +#define AA_MAY_BE_READ AA_MAY_CREATE +#define PTRACE_PERM_SHIFT 2 -int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, - unsigned int mode); +#define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) + +int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request); #endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h new file mode 100644 index 000000000000..9a283b722755 --- /dev/null +++ b/security/apparmor/include/label.h @@ -0,0 +1,441 @@ +/* + * AppArmor security module + * + * This file contains AppArmor label definitions + * + * Copyright 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_LABEL_H +#define __AA_LABEL_H + +#include <linux/atomic.h> +#include <linux/audit.h> +#include <linux/rbtree.h> +#include <linux/rcupdate.h> + +#include "apparmor.h" +#include "lib.h" + +struct aa_ns; + +#define LOCAL_VEC_ENTRIES 8 +#define DEFINE_VEC(T, V) \ + struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \ + struct aa_ ## T **(V) + +#define vec_setup(T, V, N, GFP) \ +({ \ + if ((N) <= LOCAL_VEC_ENTRIES) { \ + typeof(N) i; \ + (V) = (_ ## V ## _localtmp); \ + for (i = 0; i < (N); i++) \ + (V)[i] = NULL; \ + } else \ + (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \ + (V) ? 0 : -ENOMEM; \ +}) + +#define vec_cleanup(T, V, N) \ +do { \ + int i; \ + for (i = 0; i < (N); i++) { \ + if (!IS_ERR_OR_NULL((V)[i])) \ + aa_put_ ## T((V)[i]); \ + } \ + if ((V) != _ ## V ## _localtmp) \ + kfree(V); \ +} while (0) + +#define vec_last(VEC, SIZE) ((VEC)[(SIZE) - 1]) +#define vec_ns(VEC, SIZE) (vec_last((VEC), (SIZE))->ns) +#define vec_labelset(VEC, SIZE) (&vec_ns((VEC), (SIZE))->labels) +#define cleanup_domain_vec(V, L) cleanup_label_vec((V), (L)->size) + +struct aa_profile; +#define VEC_FLAG_TERMINATE 1 +int aa_vec_unique(struct aa_profile **vec, int n, int flags); +struct aa_label *aa_vec_find_or_create_label(struct aa_profile **vec, int len, + gfp_t gfp); +#define aa_sort_and_merge_vec(N, V) \ + aa_sort_and_merge_profiles((N), (struct aa_profile **)(V)) + + +/* struct aa_labelset - set of labels for a namespace + * + * Labels are reference counted; aa_labelset does not contribute to label + * reference counts. Once a label's last refcount is put it is removed from + * the set. + */ +struct aa_labelset { + rwlock_t lock; + + struct rb_root root; +}; + +#define __labelset_for_each(LS, N) \ + for ((N) = rb_first(&(LS)->root); (N); (N) = rb_next(N)) + +void aa_labelset_destroy(struct aa_labelset *ls); +void aa_labelset_init(struct aa_labelset *ls); + + +enum label_flags { + FLAG_HAT = 1, /* profile is a hat */ + FLAG_UNCONFINED = 2, /* label unconfined only if all */ + FLAG_NULL = 4, /* profile is null learning profile */ + FLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ + FLAG_IMMUTIBLE = 0x10, /* don't allow changes/replacement */ + FLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ + FLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ + FLAG_NS_COUNT = 0x80, /* carries NS ref count */ + FLAG_IN_TREE = 0x100, /* label is in tree */ + FLAG_PROFILE = 0x200, /* label is a profile */ + FLAG_EXPLICIT = 0x400, /* explicit static label */ + FLAG_STALE = 0x800, /* replaced/removed */ + FLAG_RENAMED = 0x1000, /* label has renaming in it */ + FLAG_REVOKED = 0x2000, /* label has revocation in it */ + + /* These flags must correspond with PATH_flags */ + /* TODO: add new path flags */ +}; + +struct aa_label; +struct aa_proxy { + struct kref count; + struct aa_label __rcu *label; +}; + +struct label_it { + int i, j; +}; + +/* struct aa_label - lazy labeling struct + * @count: ref count of active users + * @node: rbtree position + * @rcu: rcu callback struct + * @proxy: is set to the label that replaced this label + * @hname: text representation of the label (MAYBE_NULL) + * @flags: stale and other flags - values may change under label set lock + * @secid: secid that references this label + * @size: number of entries in @ent[] + * @ent: set of profiles for label, actual size determined by @size + */ +struct aa_label { + struct kref count; + struct rb_node node; + struct rcu_head rcu; + struct aa_proxy *proxy; + __counted char *hname; + long flags; + u32 secid; + int size; + struct aa_profile *vec[]; +}; + +#define last_error(E, FN) \ +do { \ + int __subE = (FN); \ + if (__subE) \ + (E) = __subE; \ +} while (0) + +#define label_isprofile(X) ((X)->flags & FLAG_PROFILE) +#define label_unconfined(X) ((X)->flags & FLAG_UNCONFINED) +#define unconfined(X) label_unconfined(X) +#define label_is_stale(X) ((X)->flags & FLAG_STALE) +#define __label_make_stale(X) ((X)->flags |= FLAG_STALE) +#define labels_ns(X) (vec_ns(&((X)->vec[0]), (X)->size)) +#define labels_set(X) (&labels_ns(X)->labels) +#define labels_profile(X) ((X)->vec[(X)->size - 1]) + + +int aa_label_next_confined(struct aa_label *l, int i); + +/* for each profile in a label */ +#define label_for_each(I, L, P) \ + for ((I).i = 0; ((P) = (L)->vec[(I).i]); ++((I).i)) + +/* assumes break/goto ended label_for_each */ +#define label_for_each_cont(I, L, P) \ + for (++((I).i); ((P) = (L)->vec[(I).i]); ++((I).i)) + +#define next_comb(I, L1, L2) \ +do { \ + (I).j++; \ + if ((I).j >= (L2)->size) { \ + (I).i++; \ + (I).j = 0; \ + } \ +} while (0) + + +/* for each combination of P1 in L1, and P2 in L2 */ +#define label_for_each_comb(I, L1, L2, P1, P2) \ +for ((I).i = (I).j = 0; \ + ((P1) = (L1)->vec[(I).i]) && ((P2) = (L2)->vec[(I).j]); \ + (I) = next_comb(I, L1, L2)) + +#define fn_for_each_comb(L1, L2, P1, P2, FN) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each_comb(i, (L1), (L2), (P1), (P2)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +/* for each profile that is enforcing confinement in a label */ +#define label_for_each_confined(I, L, P) \ + for ((I).i = aa_label_next_confined((L), 0); \ + ((P) = (L)->vec[(I).i]); \ + (I).i = aa_label_next_confined((L), (I).i + 1)) + +#define label_for_each_in_merge(I, A, B, P) \ + for ((I).i = (I).j = 0; \ + ((P) = aa_label_next_in_merge(&(I), (A), (B))); \ + ) + +#define label_for_each_not_in_set(I, SET, SUB, P) \ + for ((I).i = (I).j = 0; \ + ((P) = __aa_label_next_not_in_set(&(I), (SET), (SUB))); \ + ) + +#define next_in_ns(i, NS, L) \ +({ \ + typeof(i) ___i = (i); \ + while ((L)->vec[___i] && (L)->vec[___i]->ns != (NS)) \ + (___i)++; \ + (___i); \ +}) + +#define label_for_each_in_ns(I, NS, L, P) \ + for ((I).i = next_in_ns(0, (NS), (L)); \ + ((P) = (L)->vec[(I).i]); \ + (I).i = next_in_ns((I).i + 1, (NS), (L))) + +#define fn_for_each_in_ns(L, P, FN) \ +({ \ + struct label_it __i; \ + struct aa_ns *__ns = labels_ns(L); \ + int __E = 0; \ + label_for_each_in_ns(__i, __ns, (L), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + + +#define fn_for_each_XXX(L, P, FN, ...) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each ## __VA_ARGS__(i, (L), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +#define fn_for_each(L, P, FN) fn_for_each_XXX(L, P, FN) +#define fn_for_each_confined(L, P, FN) fn_for_each_XXX(L, P, FN, _confined) + +#define fn_for_each2_XXX(L1, L2, P, FN, ...) \ +({ \ + struct label_it i; \ + int __E = 0; \ + label_for_each ## __VA_ARGS__(i, (L1), (L2), (P)) { \ + last_error(__E, (FN)); \ + } \ + __E; \ +}) + +#define fn_for_each_in_merge(L1, L2, P, FN) \ + fn_for_each2_XXX((L1), (L2), P, FN, _in_merge) +#define fn_for_each_not_in_set(L1, L2, P, FN) \ + fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set) + +#define LABEL_MEDIATES(L, C) \ +({ \ + struct aa_profile *profile; \ + struct label_it i; \ + int ret = 0; \ + label_for_each(i, (L), profile) { \ + if (PROFILE_MEDIATES(profile, (C))) { \ + ret = 1; \ + break; \ + } \ + } \ + ret; \ +}) + + +void aa_labelset_destroy(struct aa_labelset *ls); +void aa_labelset_init(struct aa_labelset *ls); +void __aa_labelset_update_subtree(struct aa_ns *ns); + +void aa_label_free(struct aa_label *label); +void aa_label_kref(struct kref *kref); +bool aa_label_init(struct aa_label *label, int size); +struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp); + +bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub); +struct aa_profile *__aa_label_next_not_in_set(struct label_it *I, + struct aa_label *set, + struct aa_label *sub); +bool aa_label_remove(struct aa_label *label); +struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *l); +bool aa_label_replace(struct aa_label *old, struct aa_label *new); +bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old, + struct aa_label *new); + +struct aa_label *aa_label_find(struct aa_label *l); + +struct aa_profile *aa_label_next_in_merge(struct label_it *I, + struct aa_label *a, + struct aa_label *b); +struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b); +struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b, + gfp_t gfp); + + +bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp); + +#define FLAGS_NONE 0 +#define FLAG_SHOW_MODE 1 +#define FLAG_VIEW_SUBNS 2 +#define FLAG_HIDDEN_UNCONFINED 4 +int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, + struct aa_label *label, int flags); +int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, + int flags, gfp_t gfp); +int aa_label_acntsxprint(char __counted **strp, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, + struct aa_label *label, int flags, gfp_t gfp); +void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, + gfp_t gfp); +void aa_label_audit(struct audit_buffer *ab, struct aa_label *label, gfp_t gfp); +void aa_label_seq_print(struct seq_file *f, struct aa_label *label, gfp_t gfp); +void aa_label_printk(struct aa_label *label, gfp_t gfp); + +struct aa_label *aa_label_parse(struct aa_label *base, const char *str, + gfp_t gfp, bool create, bool force_stack); + + +struct aa_perms; +int aa_label_match(struct aa_profile *profile, struct aa_label *label, + unsigned int state, bool subns, u32 request, + struct aa_perms *perms); + + +/** + * __aa_get_label - get a reference count to uncounted label reference + * @l: reference to get a count on + * + * Returns: pointer to reference OR NULL if race is lost and reference is + * being repeated. + * Requires: lock held, and the return code MUST be checked + */ +static inline struct aa_label *__aa_get_label(struct aa_label *l) +{ + if (l && kref_get_unless_zero(&l->count)) + return l; + + return NULL; +} + +static inline struct aa_label *aa_get_label(struct aa_label *l) +{ + if (l) + kref_get(&(l->count)); + + return l; +} + + +/** + * aa_get_label_rcu - increment refcount on a label that can be replaced + * @l: pointer to label that can be replaced (NOT NULL) + * + * Returns: pointer to a refcounted label. + * else NULL if no label + */ +static inline struct aa_label *aa_get_label_rcu(struct aa_label __rcu **l) +{ + struct aa_label *c; + + rcu_read_lock(); + do { + c = rcu_dereference(*l); + } while (c && !kref_get_unless_zero(&c->count)); + rcu_read_unlock(); + + return c; +} + +/** + * aa_get_newest_label - find the newest version of @l + * @l: the label to check for newer versions of + * + * Returns: refcounted newest version of @l taking into account + * replacement, renames and removals + * return @l. + */ +static inline struct aa_label *aa_get_newest_label(struct aa_label *l) +{ + if (!l) + return NULL; + + if (label_is_stale(l)) { + struct aa_label *tmp; + + AA_BUG(!l->proxy); + AA_BUG(!l->proxy->label); + /* BUG: only way this can happen is @l ref count and its + * replacement count have gone to 0 and are on their way + * to destruction. ie. we have a refcounting error + */ + tmp = aa_get_label_rcu(&l->proxy->label); + AA_BUG(!tmp); + + return tmp; + } + + return aa_get_label(l); +} + +static inline void aa_put_label(struct aa_label *l) +{ + if (l) + kref_put(&l->count, aa_label_kref); +} + + +struct aa_proxy *aa_alloc_proxy(struct aa_label *l, gfp_t gfp); +void aa_proxy_kref(struct kref *kref); + +static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *proxy) +{ + if (proxy) + kref_get(&(proxy->count)); + + return proxy; +} + +static inline void aa_put_proxy(struct aa_proxy *proxy) +{ + if (proxy) + kref_put(&proxy->count, aa_proxy_kref); +} + +void __aa_proxy_redirect(struct aa_label *orig, struct aa_label *new); + +#endif /* __AA_LABEL_H */ diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 550a700563b4..436b3a722357 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -60,6 +60,7 @@ extern int apparmor_initialized; /* fn's in lib */ +const char *skipn_spaces(const char *str, size_t n); char *aa_split_fqname(char *args, char **ns_name); const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name, size_t *ns_len); @@ -99,6 +100,36 @@ static inline bool path_mediated_fs(struct dentry *dentry) return !(dentry->d_sb->s_flags & MS_NOUSER); } + +struct counted_str { + struct kref count; + char name[]; +}; + +#define str_to_counted(str) \ + ((struct counted_str *)(str - offsetof(struct counted_str, name))) + +#define __counted /* atm just a notation */ + +void aa_str_kref(struct kref *kref); +char *aa_str_alloc(int size, gfp_t gfp); + + +static inline __counted char *aa_get_str(__counted char *str) +{ + if (str) + kref_get(&(str_to_counted(str)->count)); + + return str; +} + +static inline void aa_put_str(__counted char *str) +{ + if (str) + kref_put(&str_to_counted(str)->count, aa_str_kref); +} + + /* struct aa_policy - common part of both namespaces and profiles * @name: name of the object * @hname - The hierarchical name @@ -107,7 +138,7 @@ static inline bool path_mediated_fs(struct dentry *dentry) */ struct aa_policy { const char *name; - const char *hname; + __counted char *hname; struct list_head list; struct list_head profiles; }; @@ -180,4 +211,89 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, const char *name, gfp_t gfp); void aa_policy_destroy(struct aa_policy *policy); -#endif /* AA_LIB_H */ + +/* + * fn_label_build - abstract out the build of a label transition + * @L: label the transition is being computed for + * @P: profile parameter derived from L by this macro, can be passed to FN + * @GFP: memory allocation type to use + * @FN: fn to call for each profile transition. @P is set to the profile + * + * Returns: new label on success + * ERR_PTR if build @FN fails + * NULL if label_build fails due to low memory conditions + * + * @FN must return a label or ERR_PTR on failure. NULL is not allowed + */ +#define fn_label_build(L, P, GFP, FN) \ +({ \ + __label__ __cleanup, __done; \ + struct aa_label *__new_; \ + \ + if ((L)->size > 1) { \ + /* TODO: add cache of transitions already done */ \ + struct label_it __i; \ + int __j, __k, __count; \ + DEFINE_VEC(label, __lvec); \ + DEFINE_VEC(profile, __pvec); \ + if (vec_setup(label, __lvec, (L)->size, (GFP))) { \ + __new_ = NULL; \ + goto __done; \ + } \ + __j = 0; \ + label_for_each(__i, (L), (P)) { \ + __new_ = (FN); \ + AA_BUG(!__new_); \ + if (IS_ERR(__new_)) \ + goto __cleanup; \ + __lvec[__j++] = __new_; \ + } \ + for (__j = __count = 0; __j < (L)->size; __j++) \ + __count += __lvec[__j]->size; \ + if (!vec_setup(profile, __pvec, __count, (GFP))) { \ + for (__j = __k = 0; __j < (L)->size; __j++) { \ + label_for_each(__i, __lvec[__j], (P)) \ + __pvec[__k++] = aa_get_profile(P); \ + } \ + __count -= aa_vec_unique(__pvec, __count, 0); \ + if (__count > 1) { \ + __new_ = aa_vec_find_or_create_label(__pvec,\ + __count, (GFP)); \ + /* only fails if out of Mem */ \ + if (!__new_) \ + __new_ = NULL; \ + } else \ + __new_ = aa_get_label(&__pvec[0]->label); \ + vec_cleanup(profile, __pvec, __count); \ + } else \ + __new_ = NULL; \ +__cleanup: \ + vec_cleanup(label, __lvec, (L)->size); \ + } else { \ + (P) = labels_profile(L); \ + __new_ = (FN); \ + } \ +__done: \ + if (!__new_) \ + AA_DEBUG("label build failed\n"); \ + (__new_); \ +}) + + +#define __fn_build_in_ns(NS, P, NS_FN, OTHER_FN) \ +({ \ + struct aa_label *__new; \ + if ((P)->ns != (NS)) \ + __new = (OTHER_FN); \ + else \ + __new = (NS_FN); \ + (__new); \ +}) + +#define fn_label_build_in_ns(L, P, GFP, NS_FN, OTHER_FN) \ +({ \ + fn_label_build((L), (P), (GFP), \ + __fn_build_in_ns(labels_ns(L), (P), (NS_FN), (OTHER_FN))); \ +}) + +#endif /* __AA_LIB_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h index 0444fdde3918..05fb3305671e 100644 --- a/security/apparmor/include/path.h +++ b/security/apparmor/include/path.h @@ -23,11 +23,12 @@ enum path_flags { PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ - PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ + PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ }; -int aa_path_name(const struct path *path, int flags, char **buffer, - const char **name, const char **info); +int aa_path_name(const struct path *path, int flags, char *buffer, + const char **name, const char **info, + const char *disconnected); #define MAX_PATH_BUFFERS 2 diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h new file mode 100644 index 000000000000..2b27bb79aec4 --- /dev/null +++ b/security/apparmor/include/perms.h @@ -0,0 +1,155 @@ +/* + * AppArmor security module + * + * This file contains AppArmor basic permission sets definitions. + * + * Copyright 2017 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_PERM_H +#define __AA_PERM_H + +#include <linux/fs.h> +#include "label.h" + +#define AA_MAY_EXEC MAY_EXEC +#define AA_MAY_WRITE MAY_WRITE +#define AA_MAY_READ MAY_READ +#define AA_MAY_APPEND MAY_APPEND + +#define AA_MAY_CREATE 0x0010 +#define AA_MAY_DELETE 0x0020 +#define AA_MAY_OPEN 0x0040 +#define AA_MAY_RENAME 0x0080 /* pair */ + +#define AA_MAY_SETATTR 0x0100 /* meta write */ +#define AA_MAY_GETATTR 0x0200 /* meta read */ +#define AA_MAY_SETCRED 0x0400 /* security cred/attr */ +#define AA_MAY_GETCRED 0x0800 + +#define AA_MAY_CHMOD 0x1000 /* pair */ +#define AA_MAY_CHOWN 0x2000 /* pair */ +#define AA_MAY_CHGRP 0x4000 /* pair */ +#define AA_MAY_LOCK 0x8000 /* LINK_SUBSET overlaid */ + +#define AA_EXEC_MMAP 0x00010000 +#define AA_MAY_MPROT 0x00020000 /* extend conditions */ +#define AA_MAY_LINK 0x00040000 /* pair */ +#define AA_MAY_SNAPSHOT 0x00080000 /* pair */ + +#define AA_MAY_DELEGATE +#define AA_CONT_MATCH 0x08000000 + +#define AA_MAY_STACK 0x10000000 +#define AA_MAY_ONEXEC 0x20000000 /* either stack or change_profile */ +#define AA_MAY_CHANGE_PROFILE 0x40000000 +#define AA_MAY_CHANGEHAT 0x80000000 + +#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ + + +#define PERMS_CHRS_MASK (MAY_READ | MAY_WRITE | AA_MAY_CREATE | \ + AA_MAY_DELETE | AA_MAY_LINK | AA_MAY_LOCK | \ + AA_MAY_EXEC | AA_EXEC_MMAP | AA_MAY_APPEND) + +#define PERMS_NAMES_MASK (PERMS_CHRS_MASK | AA_MAY_OPEN | AA_MAY_RENAME | \ + AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_SETCRED | \ + AA_MAY_GETCRED | AA_MAY_CHMOD | AA_MAY_CHOWN | \ + AA_MAY_CHGRP | AA_MAY_MPROT | AA_MAY_SNAPSHOT | \ + AA_MAY_STACK | AA_MAY_ONEXEC | \ + AA_MAY_CHANGE_PROFILE | AA_MAY_CHANGEHAT) + +extern const char aa_file_perm_chrs[]; +extern const char *aa_file_perm_names[]; + +struct aa_perms { + u32 allow; + u32 audit; /* set only when allow is set */ + + u32 deny; /* explicit deny, or conflict if allow also set */ + u32 quiet; /* set only when ~allow | deny */ + u32 kill; /* set only when ~allow | deny */ + u32 stop; /* set only when ~allow | deny */ + + u32 complain; /* accumulates only used when ~allow & ~deny */ + u32 cond; /* set only when ~allow and ~deny */ + + u32 hide; /* set only when ~allow | deny */ + u32 prompt; /* accumulates only used when ~allow & ~deny */ + + /* Reserved: + * u32 subtree; / * set only when allow is set * / + */ + u16 xindex; +}; + +#define ALL_PERMS_MASK 0xffffffff +extern struct aa_perms nullperms; +extern struct aa_perms allperms; + + +#define xcheck(FN1, FN2) \ +({ \ + int e, error = FN1; \ + e = FN2; \ + if (e) \ + error = e; \ + error; \ +}) + + +/* + * TODO: update for labels pointing to labels instead of profiles + * TODO: optimize the walk, currently does subwalk of L2 for each P in L1 + * gah this doesn't allow for label compound check!!!! + */ +#define xcheck_ns_profile_profile(P1, P2, FN, args...) \ +({ \ + int ____e = 0; \ + if (P1->ns == P2->ns) \ + ____e = FN((P1), (P2), args); \ + (____e); \ +}) + +#define xcheck_ns_profile_label(P, L, FN, args...) \ +({ \ + struct aa_profile *__p2; \ + fn_for_each((L), __p2, \ + xcheck_ns_profile_profile((P), __p2, (FN), args)); \ +}) + +#define xcheck_ns_labels(L1, L2, FN, args...) \ +({ \ + struct aa_profile *__p1; \ + fn_for_each((L1), __p1, FN(__p1, (L2), args)); \ +}) + +/* Do the cross check but applying FN at the profiles level */ +#define xcheck_labels_profiles(L1, L2, FN, args...) \ + xcheck_ns_labels((L1), (L2), xcheck_ns_profile_label, (FN), args) + + +void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); +void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); +void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, + u32 chrsmask, const char **names, u32 namesmask); +void aa_apply_modes_to_perms(struct aa_profile *profile, + struct aa_perms *perms); +void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, + struct aa_perms *perms); +void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend); +void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend); +void aa_profile_match_label(struct aa_profile *profile, struct aa_label *label, + int type, u32 request, struct aa_perms *perms); +int aa_profile_label_perm(struct aa_profile *profile, struct aa_profile *target, + u32 request, int type, u32 *deny, + struct common_audit_data *sa); +int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms, + u32 request, struct common_audit_data *sa, + void (*cb)(struct audit_buffer *, void *)); +#endif /* __AA_PERM_H */ diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 67bc96afe541..17fe41a9cac3 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -29,6 +29,8 @@ #include "domain.h" #include "file.h" #include "lib.h" +#include "label.h" +#include "perms.h" #include "resource.h" @@ -47,9 +49,9 @@ extern const char *const aa_profile_mode_names[]; #define KILL_MODE(_profile) PROFILE_MODE((_profile), APPARMOR_KILL) -#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) +#define PROFILE_IS_HAT(_profile) ((_profile)->label.flags & FLAG_HAT) -#define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE) +#define profile_is_stale(_profile) (label_is_stale(&(_profile)->label)) #define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2) @@ -66,22 +68,6 @@ enum profile_mode { APPARMOR_UNCONFINED, /* profile set to unconfined */ }; -enum profile_flags { - PFLAG_HAT = 1, /* profile is a hat */ - PFLAG_NULL = 4, /* profile is null learning profile */ - PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ - PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ - PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ - PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ - PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ - PFLAG_STALE = 0x200, /* profile replaced/removed */ - PFLAG_NS_COUNT = 0x400, /* carries NS ref count */ - - /* These flags must correspond with PATH_flags */ - PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ -}; - -struct aa_profile; /* struct aa_policydb - match engine for a policy * dfa: dfa pattern match @@ -94,11 +80,6 @@ struct aa_policydb { }; -struct aa_proxy { - struct kref count; - struct aa_profile __rcu *profile; -}; - /* struct aa_data - generic data structure * key: name for retrieving this data * size: size of data in bytes @@ -115,19 +96,17 @@ struct aa_data { /* struct aa_profile - basic confinement data * @base - base components of the profile (name, refcount, lists, lock ...) - * @count: reference count of the obj - * @rcu: rcu head used when removing from @list + * @label - label this profile is an extension of * @parent: parent of profile * @ns: namespace the profile is in - * @proxy: is set to the profile that replaced this profile * @rename: optional profile name that this profile renamed * @attach: human readable attachment string * @xmatch: optional extended matching for unconfined executables names * @xmatch_len: xmatch prefix len, used to determine xmatch priority * @audit: the auditing mode of the profile * @mode: the enforcement mode of the profile - * @flags: flags controlling profile behavior * @path_flags: flags controlling path generation behavior + * @disconnected: what to prepend if attach_disconnected is specified * @size: the memory consumed by this profiles rules * @policy: general match rules governing policy * @file: The set of rules governing basic file access and domain transitions @@ -143,8 +122,6 @@ struct aa_data { * used to determine profile attachment against unconfined tasks. All other * attachments are determined by profile X transition rules. * - * The @proxy struct is write protected by the profile lock. - * * Profiles have a hierarchy where hats and children profiles keep * a reference to their parent. * @@ -154,12 +131,9 @@ struct aa_data { */ struct aa_profile { struct aa_policy base; - struct kref count; - struct rcu_head rcu; struct aa_profile __rcu *parent; struct aa_ns *ns; - struct aa_proxy *proxy; const char *rename; const char *attach; @@ -167,8 +141,8 @@ struct aa_profile { int xmatch_len; enum audit_mode audit; long mode; - long flags; u32 path_flags; + const char *disconnected; int size; struct aa_policydb policy; @@ -181,17 +155,24 @@ struct aa_profile { char *dirname; struct dentry *dents[AAFS_PROF_SIZEOF]; struct rhashtable *data; + struct aa_label label; }; extern enum profile_mode aa_g_profile_mode; -void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new); +#define AA_MAY_LOAD_POLICY AA_MAY_APPEND +#define AA_MAY_REPLACE_POLICY AA_MAY_WRITE +#define AA_MAY_REMOVE_POLICY AA_MAY_DELETE + +#define profiles_ns(P) ((P)->ns) +#define name_is_shared(A, B) ((A)->hname && (A)->hname == (B)->hname) void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); void aa_free_proxy_kref(struct kref *kref); -struct aa_profile *aa_alloc_profile(const char *name, gfp_t gfp); +struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy, + gfp_t gfp); struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, const char *base, gfp_t gfp); void aa_free_profile(struct aa_profile *profile); @@ -200,21 +181,44 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); struct aa_profile *aa_lookupn_profile(struct aa_ns *ns, const char *hname, size_t n); struct aa_profile *aa_lookup_profile(struct aa_ns *ns, const char *name); -struct aa_profile *aa_fqlookupn_profile(struct aa_profile *base, +struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, const char *fqname, size_t n); struct aa_profile *aa_match_profile(struct aa_ns *ns, const char *name); -ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, - bool noreplace, struct aa_loaddata *udata); -ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_profile *profile, - char *name, size_t size); +ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label, + u32 mask, struct aa_loaddata *udata); +ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label, + char *name, size_t size); void __aa_profile_list_release(struct list_head *head); #define PROF_ADD 1 #define PROF_REPLACE 0 -#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) +#define profile_unconfined(X) ((X)->mode == APPARMOR_UNCONFINED) + +/** + * aa_get_newest_profile - simple wrapper fn to wrap the label version + * @p: profile (NOT NULL) + * + * Returns refcount to newest version of the profile (maybe @p) + * + * Requires: @p must be held with a valid refcount + */ +static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) +{ + return labels_profile(aa_get_newest_label(&p->label)); +} +#define PROFILE_MEDIATES(P, T) ((P)->policy.start[(T)]) +/* safe version of POLICY_MEDIATES for full range input */ +static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, + unsigned char class) +{ + if (profile->policy.dfa) + return aa_dfa_match_len(profile->policy.dfa, + profile->policy.start[0], &class, 1); + return 0; +} /** * aa_get_profile - increment refcount on profile @p @@ -226,7 +230,7 @@ void __aa_profile_list_release(struct list_head *head); static inline struct aa_profile *aa_get_profile(struct aa_profile *p) { if (p) - kref_get(&(p->count)); + kref_get(&(p->label.count)); return p; } @@ -240,7 +244,7 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p) */ static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p) { - if (p && kref_get_unless_zero(&p->count)) + if (p && kref_get_unless_zero(&p->label.count)) return p; return NULL; @@ -260,53 +264,20 @@ static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p) rcu_read_lock(); do { c = rcu_dereference(*p); - } while (c && !kref_get_unless_zero(&c->count)); + } while (c && !kref_get_unless_zero(&c->label.count)); rcu_read_unlock(); return c; } /** - * aa_get_newest_profile - find the newest version of @profile - * @profile: the profile to check for newer versions of - * - * Returns: refcounted newest version of @profile taking into account - * replacement, renames and removals - * return @profile. - */ -static inline struct aa_profile *aa_get_newest_profile(struct aa_profile *p) -{ - if (!p) - return NULL; - - if (profile_is_stale(p)) - return aa_get_profile_rcu(&p->proxy->profile); - - return aa_get_profile(p); -} - -/** * aa_put_profile - decrement refcount on profile @p * @p: profile (MAYBE NULL) */ static inline void aa_put_profile(struct aa_profile *p) { if (p) - kref_put(&p->count, aa_free_profile_kref); -} - -static inline struct aa_proxy *aa_get_proxy(struct aa_proxy *p) -{ - if (p) - kref_get(&(p->count)); - - return p; -} - -static inline void aa_put_proxy(struct aa_proxy *p) -{ - if (p) - kref_put(&p->count, aa_free_proxy_kref); + kref_put(&p->label.count, aa_label_kref); } static inline int AUDIT_MODE(struct aa_profile *profile) @@ -319,7 +290,7 @@ static inline int AUDIT_MODE(struct aa_profile *profile) bool policy_view_capable(struct aa_ns *ns); bool policy_admin_capable(struct aa_ns *ns); -int aa_may_manage_policy(struct aa_profile *profile, struct aa_ns *ns, - const char *op); +int aa_may_manage_policy(struct aa_label *label, struct aa_ns *ns, + u32 mask); #endif /* __AA_POLICY_H */ diff --git a/security/apparmor/include/policy_ns.h b/security/apparmor/include/policy_ns.h index 89cffddd7e75..9605f18624e2 100644 --- a/security/apparmor/include/policy_ns.h +++ b/security/apparmor/include/policy_ns.h @@ -19,6 +19,7 @@ #include "apparmor.h" #include "apparmorfs.h" +#include "label.h" #include "policy.h" @@ -68,6 +69,11 @@ struct aa_ns { atomic_t uniq_null; long uniq_id; int level; + long revision; + wait_queue_head_t wait; + + struct aa_labelset labels; + struct list_head rawdata_list; struct dentry *dents[AAFS_NS_SIZEOF]; }; @@ -76,6 +82,8 @@ extern struct aa_ns *root_ns; extern const char *aa_hidden_ns_name; +#define ns_unconfined(NS) (&(NS)->unconfined->label) + bool aa_ns_visible(struct aa_ns *curr, struct aa_ns *view, bool subns); const char *aa_ns_name(struct aa_ns *parent, struct aa_ns *child, bool subns); void aa_free_ns(struct aa_ns *ns); @@ -85,6 +93,8 @@ void aa_free_ns_kref(struct kref *kref); struct aa_ns *aa_find_ns(struct aa_ns *root, const char *name); struct aa_ns *aa_findn_ns(struct aa_ns *root, const char *name, size_t n); +struct aa_ns *__aa_lookupn_ns(struct aa_ns *view, const char *hname, size_t n); +struct aa_ns *aa_lookupn_ns(struct aa_ns *view, const char *name, size_t n); struct aa_ns *__aa_find_or_create_ns(struct aa_ns *parent, const char *name, struct dentry *dir); struct aa_ns *aa_prepare_ns(struct aa_ns *root, const char *name); @@ -144,4 +154,15 @@ static inline struct aa_ns *__aa_find_ns(struct list_head *head, return __aa_findn_ns(head, name, strlen(name)); } +static inline struct aa_ns *__aa_lookup_ns(struct aa_ns *base, + const char *hname) +{ + return __aa_lookupn_ns(base, hname, strlen(hname)); +} + +static inline struct aa_ns *aa_lookup_ns(struct aa_ns *view, const char *name) +{ + return aa_lookupn_ns(view, name, strlen(name)); +} + #endif /* AA_NAMESPACE_H */ diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h index 4c1319eebc42..be6cd69ac319 100644 --- a/security/apparmor/include/policy_unpack.h +++ b/security/apparmor/include/policy_unpack.h @@ -17,6 +17,8 @@ #include <linux/list.h> #include <linux/kref.h> +#include <linux/dcache.h> +#include <linux/workqueue.h> struct aa_load_ent { struct list_head list; @@ -36,26 +38,84 @@ struct aa_load_ent *aa_load_ent_alloc(void); #define PACKED_MODE_KILL 2 #define PACKED_MODE_UNCONFINED 3 -/* struct aa_loaddata - buffer of policy load data set */ +struct aa_ns; + +enum { + AAFS_LOADDATA_ABI = 0, + AAFS_LOADDATA_REVISION, + AAFS_LOADDATA_HASH, + AAFS_LOADDATA_DATA, + AAFS_LOADDATA_DIR, /* must be last actual entry */ + AAFS_LOADDATA_NDENTS /* count of entries */ +}; + +/* + * struct aa_loaddata - buffer of policy raw_data set + * + * there is no loaddata ref for being on ns list, nor a ref from + * d_inode(@dentry) when grab a ref from these, @ns->lock must be held + * && __aa_get_loaddata() needs to be used, and the return value + * checked, if NULL the loaddata is already being reaped and should be + * considered dead. + */ struct aa_loaddata { struct kref count; + struct list_head list; + struct work_struct work; + struct dentry *dents[AAFS_LOADDATA_NDENTS]; + struct aa_ns *ns; + char *name; size_t size; + long revision; /* the ns policy revision this caused */ int abi; unsigned char *hash; + char data[]; }; int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns); +/** + * __aa_get_loaddata - get a reference count to uncounted data reference + * @data: reference to get a count on + * + * Returns: pointer to reference OR NULL if race is lost and reference is + * being repeated. + * Requires: @data->ns->lock held, and the return code MUST be checked + * + * Use only from inode->i_private and @data->list found references + */ +static inline struct aa_loaddata * +__aa_get_loaddata(struct aa_loaddata *data) +{ + if (data && kref_get_unless_zero(&(data->count))) + return data; + + return NULL; +} + +/** + * aa_get_loaddata - get a reference count from a counted data reference + * @data: reference to get a count on + * + * Returns: point to reference + * Requires: @data to have a valid reference count on it. It is a bug + * if the race to reap can be encountered when it is used. + */ static inline struct aa_loaddata * aa_get_loaddata(struct aa_loaddata *data) { - if (data) - kref_get(&(data->count)); - return data; + struct aa_loaddata *tmp = __aa_get_loaddata(data); + + AA_BUG(data && !tmp); + + return tmp; } +void __aa_loaddata_update(struct aa_loaddata *data, long revision); +bool aa_rawdata_eq(struct aa_loaddata *l, struct aa_loaddata *r); void aa_loaddata_kref(struct kref *kref); +struct aa_loaddata *aa_loaddata_alloc(size_t size); static inline void aa_put_loaddata(struct aa_loaddata *data) { if (data) diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h index 6bd5f33d9533..c8fd99c9357d 100644 --- a/security/apparmor/include/procattr.h +++ b/security/apparmor/include/procattr.h @@ -15,11 +15,7 @@ #ifndef __AA_PROCATTR_H #define __AA_PROCATTR_H -#define AA_DO_TEST 1 -#define AA_ONEXEC 1 - -int aa_getprocattr(struct aa_profile *profile, char **string); -int aa_setprocattr_changehat(char *args, size_t size, int test); -int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); +int aa_getprocattr(struct aa_label *label, char **string); +int aa_setprocattr_changehat(char *args, size_t size, int flags); #endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h index d3f4cf027957..76f1586c9adb 100644 --- a/security/apparmor/include/resource.h +++ b/security/apparmor/include/resource.h @@ -34,13 +34,13 @@ struct aa_rlimit { struct rlimit limits[RLIM_NLIMITS]; }; -extern struct aa_fs_entry aa_fs_entry_rlimit[]; +extern struct aa_sfs_entry aa_sfs_entry_rlimit[]; int aa_map_resource(int resource); -int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *, +int aa_task_setrlimit(struct aa_label *label, struct task_struct *task, unsigned int resource, struct rlimit *new_rlim); -void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); +void __aa_transition_rlimits(struct aa_label *old, struct aa_label *new); static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) { |