.llseek = generic_file_llseek,
};
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS)
struct timers_private {
struct pid *pid;
struct task_struct *task;
length = -ESRCH;
if (!task)
goto out_no_task;
+
+ /* A task may only write its own attributes. */
+ length = -EACCES;
+ if (current != task)
+ goto out;
+
if (count > PAGE_SIZE)
count = PAGE_SIZE;
}
/* Guard against adverse ptrace interaction */
- length = mutex_lock_interruptible(&task->signal->cred_guard_mutex);
+ length = mutex_lock_interruptible(¤t->signal->cred_guard_mutex);
if (length < 0)
goto out_free;
- length = security_setprocattr(task,
- (char*)file->f_path.dentry->d_name.name,
+ length = security_setprocattr(file->f_path.dentry->d_name.name,
page, count);
- mutex_unlock(&task->signal->cred_guard_mutex);
+ mutex_unlock(¤t->signal->cred_guard_mutex);
out_free:
kfree(page);
out:
REG("projid_map", S_IRUGO|S_IWUSR, proc_projid_map_operations),
REG("setgroups", S_IRUGO|S_IWUSR, proc_setgroups_operations),
#endif
-#ifdef CONFIG_CHECKPOINT_RESTORE
+#if defined(CONFIG_CHECKPOINT_RESTORE) && defined(CONFIG_POSIX_TIMERS)
REG("timers", S_IRUGO, proc_timers_operations),
#endif
REG("timerslack_ns", S_IRUGO|S_IWUGO, proc_pid_set_timerslack_ns_operations),
iter.tgid += 1, iter = next_tgid(ns, iter)) {
char name[PROC_NUMBUF];
int len;
+
+ cond_resched();
if (!has_pid_permissions(ns, iter.task, 2))
continue;
#include <linux/tty.h>
#include <linux/iocontext.h>
#include <linux/key.h>
- #include <linux/security.h>
#include <linux/cpu.h>
#include <linux/acct.h>
#include <linux/tsacct_kern.h>
#include <linux/shm.h>
#include <linux/kcov.h>
#include <linux/random.h>
+#include <linux/rcuwait.h>
#include <linux/uaccess.h>
#include <asm/unistd.h>
bool group_dead = thread_group_leader(tsk);
struct sighand_struct *sighand;
struct tty_struct *uninitialized_var(tty);
- cputime_t utime, stime;
+ u64 utime, stime;
sighand = rcu_dereference_check(tsk->sighand,
lockdep_tasklist_lock_is_held());
return task;
}
+void rcuwait_wake_up(struct rcuwait *w)
+{
+ struct task_struct *task;
+
+ rcu_read_lock();
+
+ /*
+ * Order condition vs @task, such that everything prior to the load
+ * of @task is visible. This is the condition as to why the user called
+ * rcuwait_trywake() in the first place. Pairs with set_current_state()
+ * barrier (A) in rcuwait_wait_event().
+ *
+ * WAIT WAKE
+ * [S] tsk = current [S] cond = true
+ * MB (A) MB (B)
+ * [L] cond [L] tsk
+ */
+ smp_rmb(); /* (B) */
+
+ /*
+ * Avoid using task_rcu_dereference() magic as long as we are careful,
+ * see comment in rcuwait_wait_event() regarding ->exit_state.
+ */
+ task = rcu_dereference(w->task);
+ if (task)
+ wake_up_process(task);
+ rcu_read_unlock();
+}
+
struct task_struct *try_get_task_struct(struct task_struct **ptask)
{
struct task_struct *task;
* Turn us into a lazy TLB process if we
* aren't already..
*/
-static void exit_mm(struct task_struct *tsk)
+static void exit_mm(void)
{
- struct mm_struct *mm = tsk->mm;
+ struct mm_struct *mm = current->mm;
struct core_state *core_state;
- mm_release(tsk, mm);
+ mm_release(current, mm);
if (!mm)
return;
sync_mm_rss(mm);
up_read(&mm->mmap_sem);
- self.task = tsk;
+ self.task = current;
self.next = xchg(&core_state->dumper.next, &self);
/*
* Implies mb(), the result of xchg() must be visible
complete(&core_state->startup);
for (;;) {
- set_task_state(tsk, TASK_UNINTERRUPTIBLE);
+ set_current_state(TASK_UNINTERRUPTIBLE);
if (!self.task) /* see coredump_finish() */
break;
freezable_schedule();
}
- __set_task_state(tsk, TASK_RUNNING);
+ __set_current_state(TASK_RUNNING);
down_read(&mm->mmap_sem);
}
atomic_inc(&mm->mm_count);
- BUG_ON(mm != tsk->active_mm);
+ BUG_ON(mm != current->active_mm);
/* more a memory barrier than a real lock */
- task_lock(tsk);
- tsk->mm = NULL;
+ task_lock(current);
+ current->mm = NULL;
up_read(&mm->mmap_sem);
enter_lazy_tlb(mm, current);
- task_unlock(tsk);
+ task_unlock(current);
mm_update_next_owner(mm);
mmput(mm);
if (test_thread_flag(TIF_MEMDIE))
tsk->exit_code = code;
taskstats_exit(tsk, group_dead);
- exit_mm(tsk);
+ exit_mm();
if (group_dead)
acct_process();
struct signal_struct *sig = p->signal;
struct signal_struct *psig = current->signal;
unsigned long maxrss;
- cputime_t tgutime, tgstime;
+ u64 tgutime, tgstime;
/*
* The resource counters for the group leader are in its
* Returns nonzero for a final return, when we have unlocked tasklist_lock.
* Returns zero if the search for a child should continue;
* then ->notask_error is 0 if @p is an eligible child,
- * or another error from security_task_wait(), or still -ECHILD.
+ * or still -ECHILD.
*/
static int wait_consider_task(struct wait_opts *wo, int ptrace,
struct task_struct *p)
if (!ret)
return ret;
- ret = security_task_wait(p);
- if (unlikely(ret < 0)) {
- /*
- * If we have not yet seen any eligible child,
- * then let this error code replace -ECHILD.
- * A permission error will give the user a clue
- * to look for security policy problems, rather
- * than for mysterious wait bugs.
- */
- if (wo->notask_error)
- wo->notask_error = ret;
- return 0;
- }
-
if (unlikely(exit_state == EXIT_TRACE)) {
/*
* ptrace == 0 means we are the natural parent. In this case
* Returns nonzero for a final return, when we have unlocked tasklist_lock.
* Returns zero if the search for a child should continue; then
* ->notask_error is 0 if there were any eligible children,
- * or another error from security_task_wait(), or still -ECHILD.
+ * or still -ECHILD.
*/
static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk)
{
--- /dev/null
-/* returns 0 if kref not incremented */
-static inline int kref_get_not0(struct kref *kref)
-{
- return atomic_inc_not_zero(&kref->refcount);
-}
-
+ /*
+ * AppArmor security module
+ *
+ * This file contains AppArmor lib definitions
+ *
+ * 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_LIB_H
+ #define __AA_LIB_H
+
+ #include <linux/slab.h>
+ #include <linux/fs.h>
+
+ #include "match.h"
+
+ /* Provide our own test for whether a write lock is held for asserts
+ * this is because on none SMP systems write_can_lock will always
+ * resolve to true, which is what you want for code making decisions
+ * based on it, but wrong for asserts checking that the lock is held
+ */
+ #ifdef CONFIG_SMP
+ #define write_is_locked(X) !write_can_lock(X)
+ #else
+ #define write_is_locked(X) (1)
+ #endif /* CONFIG_SMP */
+
+ /*
+ * DEBUG remains global (no per profile flag) since it is mostly used in sysctl
+ * which is not related to profile accesses.
+ */
+
+ #define DEBUG_ON (aa_g_debug)
+ #define dbg_printk(__fmt, __args...) pr_debug(__fmt, ##__args)
+ #define AA_DEBUG(fmt, args...) \
+ do { \
+ if (DEBUG_ON) \
+ pr_debug_ratelimited("AppArmor: " fmt, ##args); \
+ } while (0)
+
+ #define AA_WARN(X) WARN((X), "APPARMOR WARN %s: %s\n", __func__, #X)
+
+ #define AA_BUG(X, args...) AA_BUG_FMT((X), "" args)
+ #ifdef CONFIG_SECURITY_APPARMOR_DEBUG_ASSERTS
+ #define AA_BUG_FMT(X, fmt, args...) \
+ WARN((X), "AppArmor WARN %s: (" #X "): " fmt, __func__, ##args)
+ #else
+ #define AA_BUG_FMT(X, fmt, args...)
+ #endif
+
+ #define AA_ERROR(fmt, args...) \
+ pr_err_ratelimited("AppArmor: " fmt, ##args)
+
+ /* Flag indicating whether initialization completed */
+ extern int apparmor_initialized __initdata;
+
+ /* fn's in lib */
+ 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);
+ void aa_info_message(const char *str);
+ void *__aa_kvmalloc(size_t size, gfp_t flags);
+
+ static inline void *kvmalloc(size_t size)
+ {
+ return __aa_kvmalloc(size, 0);
+ }
+
+ static inline void *kvzalloc(size_t size)
+ {
+ return __aa_kvmalloc(size, __GFP_ZERO);
+ }
+
+ /**
+ * aa_strneq - compare null terminated @str to a non null terminated substring
+ * @str: a null terminated string
+ * @sub: a substring, not necessarily null terminated
+ * @len: length of @sub to compare
+ *
+ * The @str string must be full consumed for this to be considered a match
+ */
+ static inline bool aa_strneq(const char *str, const char *sub, int len)
+ {
+ return !strncmp(str, sub, len) && !str[len];
+ }
+
+ /**
+ * aa_dfa_null_transition - step to next state after null character
+ * @dfa: the dfa to match against
+ * @start: the state of the dfa to start matching in
+ *
+ * aa_dfa_null_transition transitions to the next state after a null
+ * character which is not used in standard matching and is only
+ * used to separate pairs.
+ */
+ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
+ unsigned int start)
+ {
+ /* the null transition only needs the string's null terminator byte */
+ return aa_dfa_next(dfa, start, 0);
+ }
+
+ static inline bool path_mediated_fs(struct dentry *dentry)
+ {
+ return !(dentry->d_sb->s_flags & MS_NOUSER);
+ }
+
+ /* struct aa_policy - common part of both namespaces and profiles
+ * @name: name of the object
+ * @hname - The hierarchical name
+ * @list: list policy object is on
+ * @profiles: head of the profiles list contained in the object
+ */
+ struct aa_policy {
+ const char *name;
+ const char *hname;
+ struct list_head list;
+ struct list_head profiles;
+ };
+
+ /**
+ * basename - find the last component of an hname
+ * @name: hname to find the base profile name component of (NOT NULL)
+ *
+ * Returns: the tail (base profile name) name component of an hname
+ */
+ static inline const char *basename(const char *hname)
+ {
+ char *split;
+
+ hname = strim((char *)hname);
+ for (split = strstr(hname, "//"); split; split = strstr(hname, "//"))
+ hname = split + 2;
+
+ return hname;
+ }
+
+ /**
+ * __policy_find - find a policy by @name on a policy list
+ * @head: list to search (NOT NULL)
+ * @name: name to search for (NOT NULL)
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @name or NULL if not found
+ */
+ static inline struct aa_policy *__policy_find(struct list_head *head,
+ const char *name)
+ {
+ struct aa_policy *policy;
+
+ list_for_each_entry_rcu(policy, head, list) {
+ if (!strcmp(policy->name, name))
+ return policy;
+ }
+ return NULL;
+ }
+
+ /**
+ * __policy_strn_find - find a policy that's name matches @len chars of @str
+ * @head: list to search (NOT NULL)
+ * @str: string to search for (NOT NULL)
+ * @len: length of match required
+ *
+ * Requires: rcu_read_lock be held
+ *
+ * Returns: unrefcounted policy that match @str or NULL if not found
+ *
+ * if @len == strlen(@strlen) then this is equiv to __policy_find
+ * other wise it allows searching for policy by a partial match of name
+ */
+ static inline struct aa_policy *__policy_strn_find(struct list_head *head,
+ const char *str, int len)
+ {
+ struct aa_policy *policy;
+
+ list_for_each_entry_rcu(policy, head, list) {
+ if (aa_strneq(policy->name, str, len))
+ return policy;
+ }
+
+ return NULL;
+ }
+
+ 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 */
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/kref.h>
+ #include <linux/rhashtable.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include "capability.h"
#include "domain.h"
#include "file.h"
+ #include "lib.h"
#include "resource.h"
+
+ struct aa_ns;
+
+ extern int unprivileged_userns_apparmor_policy;
+
extern const char *const aa_profile_mode_names[];
#define APPARMOR_MODE_NAMES_MAX_INDEX 4
#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
- #define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
+ #define profile_is_stale(_profile) ((_profile)->flags & PFLAG_STALE)
#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
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_INVALID = 0x200, /* profile replaced/removed */
+ PFLAG_STALE = 0x200, /* profile replaced/removed */
PFLAG_NS_COUNT = 0x400, /* carries NS ref count */
/* These flags must correspond with PATH_flags */
struct aa_profile;
- /* struct aa_policy - common part of both namespaces and profiles
- * @name: name of the object
- * @hname - The hierarchical name
- * @list: list policy object is on
- * @profiles: head of the profiles list contained in the object
- */
- struct aa_policy {
- char *name;
- char *hname;
- struct list_head list;
- struct list_head profiles;
- };
-
- /* struct aa_ns_acct - accounting of profiles in namespace
- * @max_size: maximum space allowed for all profiles in namespace
- * @max_count: maximum number of profiles that can be in this namespace
- * @size: current size of profiles
- * @count: current count of profiles (includes null profiles)
- */
- struct aa_ns_acct {
- int max_size;
- int max_count;
- int size;
- int count;
- };
-
- /* struct aa_namespace - namespace for a set of profiles
- * @base: common policy
- * @parent: parent of namespace
- * @lock: lock for modifying the object
- * @acct: accounting for the namespace
- * @unconfined: special unconfined profile for the namespace
- * @sub_ns: list of namespaces under the current namespace.
- * @uniq_null: uniq value used for null learning profiles
- * @uniq_id: a unique id count for the profiles in the namespace
- * @dents: dentries for the namespaces file entries in apparmorfs
- *
- * An aa_namespace defines the set profiles that are searched to determine
- * which profile to attach to a task. Profiles can not be shared between
- * aa_namespaces and profile names within a namespace are guaranteed to be
- * unique. When profiles in separate namespaces have the same name they
- * are NOT considered to be equivalent.
- *
- * Namespaces are hierarchical and only namespaces and profiles below the
- * current namespace are visible.
- *
- * Namespace names must be unique and can not contain the characters :/\0
- *
- * FIXME TODO: add vserver support of namespaces (can it all be done in
- * userspace?)
- */
- struct aa_namespace {
- struct aa_policy base;
- struct aa_namespace *parent;
- struct mutex lock;
- struct aa_ns_acct acct;
- struct aa_profile *unconfined;
- struct list_head sub_ns;
- atomic_t uniq_null;
- long uniq_id;
-
- struct dentry *dents[AAFS_NS_SIZEOF];
- };
-
/* struct aa_policydb - match engine for a policy
* dfa: dfa pattern match
* start: set of start states for the different classes of data
};
- struct aa_replacedby {
+ 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
+ * data: binary data
+ * head: reserved for rhashtable
+ */
+ struct aa_data {
+ char *key;
+ u32 size;
+ char *data;
+ struct rhash_head head;
+ };
+
/* struct aa_profile - basic confinement data
* @base - base components of the profile (name, refcount, lists, lock ...)
* @rcu: rcu head used when removing from @list
* @parent: parent of profile
* @ns: namespace the profile is in
- * @replacedby: is set to the profile that replaced this profile
+ * @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
*
* @dents: dentries for the profiles file entries in apparmorfs
* @dirname: name of the profile dir in apparmorfs
+ * @data: hashtable for free-form policy aa_data
*
* The AppArmor profile contains the basic confinement data. Each profile
* has a name, and exists in a namespace. The @name and @exec_match are
* used to determine profile attachment against unconfined tasks. All other
* attachments are determined by profile X transition rules.
*
- * The @replacedby struct is write protected by the profile lock.
+ * 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.
struct rcu_head rcu;
struct aa_profile __rcu *parent;
- struct aa_namespace *ns;
- struct aa_replacedby *replacedby;
+ struct aa_ns *ns;
+ struct aa_proxy *proxy;
const char *rename;
const char *attach;
struct aa_caps caps;
struct aa_rlimit rlimits;
+ struct aa_loaddata *rawdata;
unsigned char *hash;
char *dirname;
struct dentry *dents[AAFS_PROF_SIZEOF];
+ struct rhashtable *data;
};
- extern struct aa_namespace *root_ns;
extern enum profile_mode aa_g_profile_mode;
- void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
-
- bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view);
- const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child);
- int aa_alloc_root_ns(void);
- void aa_free_root_ns(void);
- void aa_free_namespace_kref(struct kref *kref);
+ void __aa_update_proxy(struct aa_profile *orig, struct aa_profile *new);
- struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
- const char *name);
+ void aa_add_profile(struct aa_policy *common, struct aa_profile *profile);
- void aa_free_replacedby_kref(struct kref *kref);
- struct aa_profile *aa_alloc_profile(const char *name);
- struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat);
+ void aa_free_proxy_kref(struct kref *kref);
+ struct aa_profile *aa_alloc_profile(const char *name, 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);
void aa_free_profile_kref(struct kref *kref);
struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name);
- struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name);
- struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name);
-
- ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace);
- ssize_t aa_remove_profiles(char *name, size_t size);
+ 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,
+ 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);
+ void __aa_profile_list_release(struct list_head *head);
#define PROF_ADD 1
#define PROF_REPLACE 0
#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
- static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
- {
- return rcu_dereference_protected(p->parent,
- mutex_is_locked(&p->ns->lock));
- }
-
/**
* aa_get_profile - increment refcount on profile @p
* @p: profile (MAYBE NULL)
*/
static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
{
- if (p && kref_get_not0(&p->count))
+ if (p && kref_get_unless_zero(&p->count))
return p;
return NULL;
rcu_read_lock();
do {
c = rcu_dereference(*p);
- } while (c && !kref_get_not0(&c->count));
+ } while (c && !kref_get_unless_zero(&c->count));
rcu_read_unlock();
return c;
if (!p)
return NULL;
- if (PROFILE_INVALID(p))
- return aa_get_profile_rcu(&p->replacedby->profile);
+ if (profile_is_stale(p))
+ return aa_get_profile_rcu(&p->proxy->profile);
return aa_get_profile(p);
}
kref_put(&p->count, aa_free_profile_kref);
}
- static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
+ 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_replacedby(struct aa_replacedby *p)
+ static inline void aa_put_proxy(struct aa_proxy *p)
{
if (p)
- kref_put(&p->count, aa_free_replacedby_kref);
- }
-
- /* requires profile list write lock held */
- static inline void __aa_update_replacedby(struct aa_profile *orig,
- struct aa_profile *new)
- {
- struct aa_profile *tmp;
- tmp = rcu_dereference_protected(orig->replacedby->profile,
- mutex_is_locked(&orig->ns->lock));
- rcu_assign_pointer(orig->replacedby->profile, aa_get_profile(new));
- orig->flags |= PFLAG_INVALID;
- aa_put_profile(tmp);
- }
-
- /**
- * aa_get_namespace - increment references count on @ns
- * @ns: namespace to increment reference count of (MAYBE NULL)
- *
- * Returns: pointer to @ns, if @ns is NULL returns NULL
- * Requires: @ns must be held with valid refcount when called
- */
- static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns)
- {
- if (ns)
- aa_get_profile(ns->unconfined);
-
- return ns;
- }
-
- /**
- * aa_put_namespace - decrement refcount on @ns
- * @ns: namespace to put reference of
- *
- * Decrement reference count of @ns and if no longer in use free it
- */
- static inline void aa_put_namespace(struct aa_namespace *ns)
- {
- if (ns)
- aa_put_profile(ns->unconfined);
+ kref_put(&p->count, aa_free_proxy_kref);
}
static inline int AUDIT_MODE(struct aa_profile *profile)
return profile->audit;
}
- bool policy_view_capable(void);
- bool policy_admin_capable(void);
- bool aa_may_manage_policy(int op);
+ 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);
#endif /* __AA_POLICY_H */