apparmor: add interface files for profiles and namespaces
[linux-2.6-block.git] / security / apparmor / include / policy.h
index b25491a3046a2a1dd14f16b31828c52d19d5df7e..5c72231d1c4268d0a8aa2525a55530b07849a514 100644 (file)
@@ -29,8 +29,8 @@
 #include "file.h"
 #include "resource.h"
 
-extern const char *const profile_mode_names[];
-#define APPARMOR_NAMES_MAX_INDEX 3
+extern const char *const aa_profile_mode_names[];
+#define APPARMOR_MODE_NAMES_MAX_INDEX 4
 
 #define PROFILE_MODE(_profile, _mode)          \
        ((aa_g_profile_mode == (_mode)) ||      \
@@ -42,6 +42,10 @@ extern const char *const profile_mode_names[];
 
 #define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT)
 
+#define PROFILE_INVALID(_profile) ((_profile)->flags & PFLAG_INVALID)
+
+#define on_list_rcu(X) (!list_empty(X) && (X)->prev != LIST_POISON2)
+
 /*
  * FIXME: currently need a clean way to replace and remove profiles as a
  * set.  It should be done at the namespace level.
@@ -52,17 +56,19 @@ enum profile_mode {
        APPARMOR_ENFORCE,       /* enforce access rules */
        APPARMOR_COMPLAIN,      /* allow and log access violations */
        APPARMOR_KILL,          /* kill task on access violation */
+       APPARMOR_UNCONFINED,    /* profile set to unconfined */
 };
 
 enum profile_flags {
        PFLAG_HAT = 1,                  /* profile is a hat */
-       PFLAG_UNCONFINED = 2,           /* profile is an unconfined profile */
        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_INVALID = 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 */
@@ -73,14 +79,12 @@ struct aa_profile;
 /* struct aa_policy - common part of both namespaces and profiles
  * @name: name of the object
  * @hname - The hierarchical name
- * @count: reference count of the obj
  * @list: list policy object is on
  * @profiles: head of the profiles list contained in the object
  */
 struct aa_policy {
        char *name;
        char *hname;
-       struct kref count;
        struct list_head list;
        struct list_head profiles;
 };
@@ -106,6 +110,8 @@ struct aa_ns_acct {
  * @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
@@ -124,11 +130,14 @@ struct aa_ns_acct {
 struct aa_namespace {
        struct aa_policy base;
        struct aa_namespace *parent;
-       rwlock_t lock;
+       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
@@ -142,8 +151,16 @@ struct aa_policydb {
 
 };
 
+struct aa_replacedby {
+       struct kref count;
+       struct aa_profile __rcu *profile;
+};
+
+
 /* 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
  * @parent: parent of profile
  * @ns: namespace the profile is in
  * @replacedby: is set to the profile that replaced this profile
@@ -160,13 +177,15 @@ struct aa_policydb {
  * @caps: capabilities for the profile
  * @rlimits: rlimits for the profile
  *
+ * @dents: dentries for the profiles file entries in apparmorfs
+ * @dirname: name of the profile dir in apparmorfs
+ *
  * 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 field is write protected by the profile lock.  Reads
- * are assumed to be atomic, and are done without locking.
+ * The @replacedby struct is write protected by the profile lock.
  *
  * Profiles have a hierarchy where hats and children profiles keep
  * a reference to their parent.
@@ -177,17 +196,19 @@ struct aa_policydb {
  */
 struct aa_profile {
        struct aa_policy base;
-       struct aa_profile *parent;
+       struct kref count;
+       struct rcu_head rcu;
+       struct aa_profile __rcu *parent;
 
        struct aa_namespace *ns;
-       struct aa_profile *replacedby;
+       struct aa_replacedby *replacedby;
        const char *rename;
 
        struct aa_dfa *xmatch;
        int xmatch_len;
        enum audit_mode audit;
-       enum profile_mode mode;
-       u32 flags;
+       long mode;
+       long flags;
        u32 path_flags;
        int size;
 
@@ -195,6 +216,9 @@ struct aa_profile {
        struct aa_file_rules file;
        struct aa_caps caps;
        struct aa_rlimit rlimits;
+
+       char *dirname;
+       struct dentry *dents[AAFS_PROF_SIZEOF];
 };
 
 extern struct aa_namespace *root_ns;
@@ -211,43 +235,11 @@ void aa_free_namespace_kref(struct kref *kref);
 struct aa_namespace *aa_find_namespace(struct aa_namespace *root,
                                       const char *name);
 
-static inline struct aa_policy *aa_get_common(struct aa_policy *c)
-{
-       if (c)
-               kref_get(&c->count);
-
-       return c;
-}
-
-/**
- * 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)
-               kref_get(&(ns->base.count));
-
-       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)
-               kref_put(&ns->base.count, aa_free_namespace_kref);
-}
 
+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_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);
@@ -259,25 +251,13 @@ ssize_t aa_remove_profiles(char *name, size_t size);
 #define PROF_ADD 1
 #define PROF_REPLACE 0
 
-#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED)
+#define unconfined(X) ((X)->mode == APPARMOR_UNCONFINED)
 
-/**
- * aa_newest_version - find the newest version of @profile
- * @profile: the profile to check for newer versions of (NOT NULL)
- *
- * Returns: newest version of @profile, if @profile is the newest version
- *          return @profile.
- *
- * NOTE: the profile returned is not refcounted, The refcount on @profile
- * must be held until the caller decides what to do with the returned newest
- * version.
- */
-static inline struct aa_profile *aa_newest_version(struct aa_profile *profile)
-{
-       while (profile->replacedby)
-               profile = profile->replacedby;
 
-       return profile;
+static inline struct aa_profile *aa_deref_parent(struct aa_profile *p)
+{
+       return rcu_dereference_protected(p->parent,
+                                        mutex_is_locked(&p->ns->lock));
 }
 
 /**
@@ -290,11 +270,65 @@ static inline struct aa_profile *aa_newest_version(struct aa_profile *profile)
 static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
 {
        if (p)
-               kref_get(&(p->base.count));
+               kref_get(&(p->count));
 
        return p;
 }
 
+/**
+ * aa_get_profile_not0 - increment refcount on profile @p found via lookup
+ * @p: profile  (MAYBE NULL)
+ *
+ * Returns: pointer to @p if @p is NULL will return NULL
+ * Requires: @p must be held with valid refcount when called
+ */
+static inline struct aa_profile *aa_get_profile_not0(struct aa_profile *p)
+{
+       if (p && kref_get_not0(&p->count))
+               return p;
+
+       return NULL;
+}
+
+/**
+ * aa_get_profile_rcu - increment a refcount profile that can be replaced
+ * @p: pointer to profile that can be replaced (NOT NULL)
+ *
+ * Returns: pointer to a refcounted profile.
+ *     else NULL if no profile
+ */
+static inline struct aa_profile *aa_get_profile_rcu(struct aa_profile __rcu **p)
+{
+       struct aa_profile *c;
+
+       rcu_read_lock();
+       do {
+               c = rcu_dereference(*p);
+       } while (c && !kref_get_not0(&c->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_INVALID(p))
+               return aa_get_profile_rcu(&p->replacedby->profile);
+
+       return aa_get_profile(p);
+}
+
 /**
  * aa_put_profile - decrement refcount on profile @p
  * @p: profile  (MAYBE NULL)
@@ -302,7 +336,58 @@ static inline struct aa_profile *aa_get_profile(struct aa_profile *p)
 static inline void aa_put_profile(struct aa_profile *p)
 {
        if (p)
-               kref_put(&p->base.count, aa_free_profile_kref);
+               kref_put(&p->count, aa_free_profile_kref);
+}
+
+static inline struct aa_replacedby *aa_get_replacedby(struct aa_replacedby *p)
+{
+       if (p)
+               kref_get(&(p->count));
+
+       return p;
+}
+
+static inline void aa_put_replacedby(struct aa_replacedby *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 = rcu_dereference(orig->replacedby->profile);
+       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);
 }
 
 static inline int AUDIT_MODE(struct aa_profile *profile)